diff --git a/Justfile b/Justfile index 7205d769a..50c8cb6a6 100644 --- a/Justfile +++ b/Justfile @@ -2,19 +2,19 @@ set shell := ["bash", "-uc"] # Migrate to local DB migrate *args: - dotenvx run -- ./mvnw flyway:migrate -Dflyway.locations=filesystem:./db {{args}} + dotenvx run -- bash ./mvnw flyway:migrate -Dflyway.locations=filesystem:./db {{args}} # Drop local DB drop *args: - dotenvx run -- ./mvnw flyway:clean -Dflyway.locations=filesystem:./db -Dflyway.cleanDisabled=false {{args}} + dotenvx run -- bash ./mvnw flyway:clean -Dflyway.locations=filesystem:./db -Dflyway.cleanDisabled=false {{args}} # Manually migrate STAGING DB if needed (CAREFUL!!!) migrate-stg *args: - dotenvx run -f .env.staging -- ./mvnw flyway:migrate -Dflyway.locations=filesystem:./db/migration {{args}} + dotenvx run -f .env.staging -- bash ./mvnw flyway:migrate -Dflyway.locations=filesystem:./db/migration {{args}} # Manually migrate PROD DB if needed (CAREFUL!!!) migrate-prod *args: - dotenvx run -f .env.production -- ./mvnw flyway:migrate -Dflyway.locations=filesystem:./db/migration {{args}} + dotenvx run -f .env.production -- bash ./mvnw flyway:migrate -Dflyway.locations=filesystem:./db/migration {{args}} # Manually copy production db to staging. (WILL DROP ALL STAGING DATA!!!) Check wiki for more details. copy-stg *args: @@ -22,7 +22,7 @@ copy-stg *args: # Run the backend Spring server backend-dev *args: - dotenvx run -f .env -f .env.shared -- ./mvnw -Dspring-boot.run.profiles=dev spring-boot:run {{args}} + dotenvx run -f .env -f .env.shared -- bash ./mvnw -Dspring-boot.run.profiles=dev spring-boot:run {{args}} # Run the leetcode auth stealer service auth-dev *args: @@ -34,30 +34,30 @@ auth-gen *args: # Run the backend Spring server with an exposed debugger at :5005 backend-dev-debug *args: - dotenvx run -- ./mvnw \ + dotenvx run -- bash ./mvnw \ -Dspring-boot.run.profiles=dev \ -Dspring-boot.run.jvmArguments="-agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=*:5005" \ spring-boot:run {{args}} # Builds and installs Spring backend backend-install *args: - ./mvnw install -DskipTests=true {{args}} + bash ./mvnw install -DskipTests=true {{args}} # Run backend formatter (check only) backend-spotless *args: - ./mvnw spotless:check + bash ./mvnw spotless:check # Run backend formatter (check & write) backend-spotless-fix *args: - ./mvnw spotless:apply + bash ./mvnw spotless:apply # Run backend linter, formatter, & tests backend-test *args: - just backend-spotless && dotenvx run -f .env -f .env.shared -- ./mvnw checkstyle:check verify -Dspring.profiles.active=ci {{args}} + just backend-spotless && dotenvx run -f .env -f .env.shared -- bash ./mvnw checkstyle:check verify -Dspring.profiles.active=ci {{args}} # Run backend tests with a debugger backend-testd *args: - just backend-spotless && dotenvx run -f .env -f .env.shared -- ./mvnw checkstyle:check verify -Dspring.profiles.active=ci -Dmaven.surefire.debug {{args}} + just backend-spotless && dotenvx run -f .env -f .env.shared -- bash ./mvnw checkstyle:check verify -Dspring.profiles.active=ci -Dmaven.surefire.debug {{args}} # Run the frontend frontend-dev *args: diff --git a/README.md b/README.md index 829614ca0..fd516474d 100644 --- a/README.md +++ b/README.md @@ -28,17 +28,17 @@ _Last updated: 02/14/2026_ Codebloom features include -- [User profiles with submission history](./docs/features/USER-PROFILE.md) -- [Automatic LeetCode syncing in the background](./docs/features/BGSYNC.md) -- [University-specific leaderboards](./docs/features/LEADERBOARD.md) -- [Problem Of The Day](./docs/features/POTD.md) -- [Automatic achievements](./docs/features/ACHIEVEMENTS.md) -- [Embeddable widgets](./docs/features/EMBED.md) -- [Dynamic question scoring](./docs/features/SCORES.md) -- [Live multiplayer duels](./docs/features/DUELS.md) -- [Club/organization integrations via Discord servers](./docs/features/DISCORD-INTEGRATIONS.md) - -[Goto `docs/features` to view all feature documentation](./docs/features) +- [User profiles with submission history](https://docs.patinanetwork.org/codebloom/features/USER-PROFILE/) +- [Automatic LeetCode syncing in the background](https://docs.patinanetwork.org/codebloom/features/BGSYNC/) +- [University-specific leaderboards](https://docs.patinanetwork.org/codebloom/features/LEADERBOARD/) +- [Problem Of The Day](https://docs.patinanetwork.org/codebloom/features/POTD/) +- [Automatic achievements](https://docs.patinanetwork.org/codebloom/features/ACHIEVEMENTS/) +- [Embeddable widgets](https://docs.patinanetwork.org/codebloom/features/EMBED/) +- [Dynamic question scoring](https://docs.patinanetwork.org/codebloom/features/SCORES/) +- [Live multiplayer duels](https://docs.patinanetwork.org/codebloom/features/DUELS/) +- [Club/organization integrations via Discord servers](https://docs.patinanetwork.org/codebloom/features/DISCORD-INTEGRATIONS/) + +[View all Codebloom documentation](https://docs.patinanetwork.org/codebloom/) Codebloom is a public-first platform that does not require authentication to view most data, so please feel free to click around at [codebloom.patinanetwork.org](https://codebloom.patinanetwork.org)! @@ -72,16 +72,16 @@ This repository is a monorepo that contains many different pieces of Codebloom, - [Goto `.github/scripts/redeploy/` to view Bun Shell redeploy scripts](./.github/scripts/redeploy/) - [Goto `infra/`](./infra/) - [View detailed documentation](./infra/README.md) - - [View observability documentation](./docs/observability/README.md) + - [View observability documentation](https://docs.patinanetwork.org/codebloom/observability/) - Our internal tools (e.g. custom Ink CLI scripts & our internal standup bot) are currently being written in [Rust](https://rust-lang.org/) and [TypeScript](https://typescriptlang.org) - [Goto `internal/`](./internal/) - [View detailed documentation](./internal/README.md) - Environment secrets live in local env files generated from Azure. - - [View detailed documentation](./docs/local/SECRET.md) + - [View detailed documentation](https://docs.patinanetwork.org/codebloom/local/SECRET/) ## Setup -Please go to [`docs/setup/README.md`](./docs/local/README.md) to begin setup instructions for local development. +Please go to the [local development docs](https://docs.patinanetwork.org/codebloom/local/) to begin setup instructions for local development. ## Authors diff --git a/docs/QUICK-LINKS.md b/docs/QUICK-LINKS.md deleted file mode 100644 index 223fbda0d..000000000 --- a/docs/QUICK-LINKS.md +++ /dev/null @@ -1,10 +0,0 @@ -# Quick Links - -- [Codebloom](https://codebloom.patinanetwork.org) ↗ -- [Codebloom - Swagger Documentation](https://codebloom.patinanetwork.org/swagger-ui/index.html) ↗ -- [Codebloom - Staging](https://stg.codebloom.patinanetwork.org) ↗ -- [Codebloom - Staging - Swagger Documentation](https://stg.codebloom.patinanetwork.org/swagger-ui/index.html) ↗ -- [Codebloom - Local - Swagger Documentation](http://localhost:8080/swagger-ui/index.html) ↗ -- [Codebloom - Local - Prometheus Metrics](http://localhost:8080/actuator/prometheus) ↗ -- [Grafana Dashboard - Separate domain](https://monitor.tahmid.io) -- [Public Taskboard (Read-only)](https://codebloom.notion.site) ↗ diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 000000000..2d7b4a1c9 --- /dev/null +++ b/docs/README.md @@ -0,0 +1,4 @@ +# Documentation + +Documentation can be found at https://docs.patinanetwork.org/codebloom. + diff --git a/docs/ci/AUTO_APPROVALS.md b/docs/ci/AUTO_APPROVALS.md deleted file mode 100644 index fdf72cd58..000000000 --- a/docs/ci/AUTO_APPROVALS.md +++ /dev/null @@ -1,32 +0,0 @@ -# Auto Approvals - -The auto-approval script ([.github/scripts/auto-approval/index.ts](.github/scripts/auto-approval/index.ts)) automatically approves pull requests based on two conditions. - -## How It Works - -When triggered, the script: - -1. Accepts `--githubToken`, `--repo`, and `--prId` as arguments. -2. Loads `NOTION_TASK_DB_ID` and `NOTION_SECRET` from the env. -3. Looks up the Notion task linked to the PR and reads its **Priority** property. -4. Fetches the PR from GitHub to get the author's username. -5. Checks whether the author is a repository collaborator. - -## Approval Conditions - -A PR is auto-approved if **either** condition is met: - -| Condition | Requirement | -|---|---| -| **Support ticket override** | PR's linked Notion task has Priority = `Support` **and** the PR author is a repository collaborator | -| **Owner override** | PR author is an `AUTHORIZED_USER` | - -If neither condition is met, the script logs `"Skipping auto approval..."` and exits without approving. - -## Required Secrets / Environment - -| Variable | Source | Description | -|---|---|---| -| `GH_TOKEN` | CI secret | GitHub token used to fetch PR info and submit the review | -| `NOTION_TASK_DB_ID` | `.env.ci` | Notion database ID for tasks | -| `NOTION_SECRET` | `.env.ci` | Notion integration secret | diff --git a/docs/features/ACHIEVEMENTS.md b/docs/features/ACHIEVEMENTS.md deleted file mode 100644 index 907e7ed51..000000000 --- a/docs/features/ACHIEVEMENTS.md +++ /dev/null @@ -1,20 +0,0 @@ -# Achievements - -Codebloom - Tahmid's Profile Page with Announcement -
- - - Page can be found on - - codebloom.patinanetwork.org/user/2f709448-2635-4651-9843-301c2128a011 - - - -
-
- -Announcements are automatically provided to a given user at the end of a leaderboard. A user can receive an announcement for the following condition(s): - -- If a user is in the top 3 of the global leaderboard against all users -- If a user is in the top 3 of any university/club/organization leaderboard against it's users - - A user can win multiple awards in this category from one leaderboard (one per filter). diff --git a/docs/features/BGSYNC.md b/docs/features/BGSYNC.md deleted file mode 100644 index e673ef115..000000000 --- a/docs/features/BGSYNC.md +++ /dev/null @@ -1,30 +0,0 @@ -# Background Sync - -## Submissions - -Codebloom will automatically sync your submissions from [leetcode.com](https://leetcode.com/) in the background once you have signed up and completed the short Codebloom onboarding. - -_Last updated: 02/15/2026_ - -This background sync runs approximately every 30 minutes. - -## Gotchas - -The full submission flow is somewhat asynchronous, which can lead to some confusing behavior sometimes where a user might have a submission registered, but their code is not visible. - -This is because when a user's submissions are fetched, we retrieve their 20 most recent submissions from [leetcode.com](https://leetcode.com) (20 is the maximum we can get at a given time). - -These 20 submissions are automatically parsed out by our scorer logic (see [`./SCORES.md`](./SCORES.md) for more details). - -However, these 20 submissions retrieved from [leetcode.com](https://leetcode.com) do not include `code`, `runtime`, `memory`, and more. To retrieve these fields, you must make another call - one for each submission. - -This is a problem because even with only 200 users \* 20 submissions = 400 calls in a short period of time. This was causing us to get rate-limited hard by [leetcode.com](https://leetcode.com), especially since this is considered a sensitive query, and as such, I made the decision to split this flow into two parts: - -1. Get submissions, process immediately and provide points - - We want to provide immediate and fast feedback + this network call is cheap and inexpensive. -2. For each submission, create a `Job` entry that points to `Question` - - This kicks off a background service that is listening for any notifications via PostgreSQL `NOTIFY/LISTEN`. - - Once a notification is received, it wakes the `Job` processor up and will continue to process `Job` entries until there is none left. - - This processor is slowed down much more since we usually have a huge batch of requests and there is not as much of a need to process these requests quickly - -Before this implementation, we were frequently getting rate-limited at 50 users - now we are at 235 and we have not faced said problem again since. diff --git a/docs/features/DISCORD-INTEGRATIONS.md b/docs/features/DISCORD-INTEGRATIONS.md deleted file mode 100644 index 0f34232be..000000000 --- a/docs/features/DISCORD-INTEGRATIONS.md +++ /dev/null @@ -1,82 +0,0 @@ -> [!CAUTION] -> We apologize but this page is currently a work in progress -> Reason: Details are rapidly changing, which can lead to outdated information - -# Discord Integrations - -Codebloom has various Discord integrations that can be setup to work with any club/university/organization Discord server. - -_Last updated: 02/16/2026_ - -As of right now, the Codebloom bot is currently integrated with: - -- [Patina Network](https://www.patinanetwork.org/) -- [MHC++ - Hunter College](https://www.linkedin.com/company/mhcplusplus/) -- [Girls Who Code - Hunter College](https://girlswhocode.com/) - -> [!NOTE] -> Would you like to register your club/university/organization? If so, please open an [issue](https://github.com/tahminator/codebloom/issues/new) here. - -## Automatic messages - -This is currently the main integration that Codebloom provides. - -### Weekly Leaderboard Updates - -This integration will send a weekly update every Saturday at noon (not very strict) about how the current users of the Discord server are performing. - -Here is an example of what it may look like: - -
- Discord weekly update -

- - NOTE: The role mentions would point to actual users in production. - -

-

- - NOTE: This screenshot is taken locally to avoid releasing private information from production. - -

-
- -### End of Leaderboard Winners - -This integration will send an end of leaderboard, indicating the top 3 winners from this specific Discord server. - -Here is an example of what it may look like: - -
- Discord /command -

- - NOTE: The role mentions would point to actual users in production. - -

-

- - NOTE: This screenshot is taken locally to avoid releasing private information from production. - -

-
- -## Slash commands - -Codebloom is slowly rolling out slash command support for our Discord integrations! - -### `/leaderboard` - Return leaderboard update - -Discord /command - -This slash command will return a refreshed leaderboard embed (similar to the automatic leaderboard embeds) - -Current restrcitions: - -- We cannot currently update the leaderboard via Discord (though we are working on that now!) -- If the club requested a leaderboard update before 5 minutes have elapsed, the command will then fail & indicate how long to wait until you can request a new update. - - **NOTE**: it will not show the fail response to every user, but only to the user who triggered the command. - -Here is an example of what it may look like: - -Discord /command output diff --git a/docs/features/DUELS.md b/docs/features/DUELS.md deleted file mode 100644 index 7d7c4b8a0..000000000 --- a/docs/features/DUELS.md +++ /dev/null @@ -1,5 +0,0 @@ -> [!CAUTION] -> We apologize but this page is currently a work in progress -> Reason: Currently still in development - -# Live Duels diff --git a/docs/features/EMBED.md b/docs/features/EMBED.md deleted file mode 100644 index ae5a0f9ec..000000000 --- a/docs/features/EMBED.md +++ /dev/null @@ -1,40 +0,0 @@ -# Embeddable widgets - -Patina Network - Codebloom page -
- - - Embedded widget showcase used on - - patinanetwork.org/programs/codebloom - - - -
-
- -Codebloom currently has two embeddable routes, which are explained below. These routes have been properly secured against CSRF and XSS and as such, can be embedded into any website. - -We manually add these routes via [SecurityConfig.java](./../../src/main/java/org/patinanetwork/codebloom/api/auth/security/SecurityConfig.java#L56-L65) in order to ensure that we don't accidentally open any more routes than we expected. - -## `/embed/leaderboard` - -This endpoint returns a custom leaderboard that is designed to be embeddable. - -This endpoint supports a couple special properties via URL params: - -- `pageSize` allows you to change the size of the pagination (default: `20`, max: `20`) -- `filterName=true` will only return the leaderboard for that specific filter instead. It will also render the icon of that specific integration next to our Codebloom (see left side of screenshot above). - - You cannot use more than one `filterName` at a time. If you do, it will remove all `filterName` from the dashboard - -[Goto `codebloom.patinanetwork.org/embed/leaderboard?patina=true&pageSize=5`](https://codebloom.patinanetwork.org/embed/leaderboard?patina=true&pageSize=5) to view the embed using some test parameters - -## `/embed/potd` - -This endpoint is a basic view that always returns the current Problem Of The Day (visit [`./POTD.md`](./POTD.md) to learn more). - -Unlike the traditional Problem Of The Day that is visible on the dashboard page, this doesn't use any authenticated information to hide the Problem Of The Day if you have already solved it. - -It is safe to assume that this endpoint will always show the current Problem Of The Day within the precision of a couple minutes. - -[Goto `codebloom.patinanetwork.org/embed/potd`](https://codebloom.patinanetwork.org/embed/potd) diff --git a/docs/features/LEADERBOARD.md b/docs/features/LEADERBOARD.md deleted file mode 100644 index 43950bd2b..000000000 --- a/docs/features/LEADERBOARD.md +++ /dev/null @@ -1,48 +0,0 @@ -# Leaderboard - -Patina Network - Codebloom page -
- - - Leaderboard page found on - - codebloom.patinanetwork.org/leaderboard - - - -
-
- -The leaderboard pages are one of the most important features to Codebloom, as it directly allows users to compete within: - -- the global leaderboard against all users -- any university leaderbaord they are a part of against all students in the university -- any clubs they are a part of against all members of the club - -and any combination of them for that matter - you can mix and match filters to compare user performances. You can even turn on one or many filters & enable `Toggle Global Index`: - -Toggle Global Rank with Cornell and Columbia filter enabled - -which will show you the same users, but their ranks will now indicate how they are doing across ALL users in Codebloom. - -The leaderboard pages are fully paginated, with custom filters & search queries. - -> [!NOTE] - -## `/leaderboard` - Current leaderboard - -This page will always show the currently active leaderboard. - -[Goto `codebloom.patinanetwork.org/leaderboard`](https://codebloom.patinanetwork.org/leaderboard) - -## `/leaderboard/all` - List of all leaderboards - -This page will show the list of all leaderboards. You can click on a previous leaderboard to view the leaderboard exactly as that leaderboard ended. - -[Goto `codebloom.patinanetwork.org/leaderboard/all`](https://codebloom.patinanetwork.org/leaderboard/all) - -## `/leaderboard/{leaderboardId}` - View specific ID - -This page will show the leaderboard which is connected to `{leaderboardId}`. This is in full feature parity with `/leaderboard`. - -[Goto `codebloom.patinanetwork.org/leaderboard/dcf49998-604b-4240-8711-4456c49d79e7`](https://codebloom.patinanetwork.org/leaderboard/dcf49998-604b-4240-8711-4456c49d79e7), the very first Codebloom leaderboard named `Beta Launch Leaderboard` diff --git a/docs/features/POTD.md b/docs/features/POTD.md deleted file mode 100644 index 98c26575d..000000000 --- a/docs/features/POTD.md +++ /dev/null @@ -1,35 +0,0 @@ -# POTD - Problem Of The Day - -
- Embed POTD - POTD on dashboard - not solved - POTD on dashboard - solved -
- -The Problem Of The Day (hereby referred to as POTD) is a feature on Codebloom that provides users with a score multiplier if solved. - -The user is only eligible if: - -- The user submits a solution to Codebloom while the problem is still a POTD -- The user has not solved this problem before - -This POTD can be viewed on [codebloom.patinanetwork.org/dashboard](https://codebloom.patinanetwork.org/dashboard) after onboarding steps have been completed (automatically shown to you). - -## Origin - -The POTD is a random problem every day on [leetcode.com](https://leetcode.com) that, when solved, increases your streak: - -
- leetcode.com POTD -
-Clicking on the streak icon takes you to the POTD - -The problem can also be found on [leetcode.com/problemset](https://leetcode.com/problemset/) as the first problem in the list with a calendar icon next to it. - -leetcode.com POTD - -## Embedded POTD - -There is an embedded widget for the POTD, which can be attached to any website. - -[Goto `./EMBED.md` to learn more](./EMBED.md) diff --git a/docs/features/SCORES.md b/docs/features/SCORES.md deleted file mode 100644 index 95764d1a6..000000000 --- a/docs/features/SCORES.md +++ /dev/null @@ -1,23 +0,0 @@ -# Scores - -Codebloom has some custom algebraic functions to determine how points should be allocated depending on various properties. - -> [!NOTE] -> We are always trying to improve our scoring formula, so if you have any suggestions - please open an [issue](https://github.com/tahminator/codebloom/issues/new) here. - -The actual file used to calculate scores is called [`ScoreCalculator.java`](../../src/main/java/org/patinanetwork/codebloom/common/leetcode/score/ScoreCalculator.java). This class essentially requires 3 properties to produce a score: - -- Difficulty (`EASY`, `MEDIUM`, `HARD`) -- Acceptance Rate (`0.0` to `1.0`) -- Multiplier (usually `1.0` unless it's a POTD problem which is then defined off mapped values based on problem difficulty + 5% deviation) - -You may notice that we call a `purpleFunction`, `blueFunction`, etc. These are custom algebraic functions we have defined based off of previous feedback & internal standards for what we think makes the most sense. - -These functions have a Desmos link to correlate the code to the graph inside of [`MultiplierFunctions.java`](../../src/main/java/org/patinanetwork/codebloom/common/leetcode/score/MultiplierFunctions.java), so feel free to view them to get the latest representation of any changes. - -_Last updated: 02/15/2026_ - -Here is a current screenshot: -Codebloom - Scoring Functions in Desmos - -Finally, we then apply a 5% deviation to the final result to have more variance in the outputted scores. diff --git a/docs/features/USER-PROFILE.md b/docs/features/USER-PROFILE.md deleted file mode 100644 index ab83beb0c..000000000 --- a/docs/features/USER-PROFILE.md +++ /dev/null @@ -1,18 +0,0 @@ -# User profile - -
- Landing - Leaderboard - Dashboard - Dashboard -
- -The user and the data connected to said user are integral to Codebloom. The screenshots above show the user profile of [@tahminator](https://github.com/tahminator), his list of submissions, and 2 screenshots showing an example submission with a description and code. - -This data is fetched from [leetcode.com](https://leetcode.com) and is then stored in our database for processing & viewing. - -[Goto `./SCORES.md` to learn more about how we score submissions](./SCORES.md) - -[Goto `./LEADERBOARD.md` to learn more about how our leaderboard works](./LEADERBOARD.md) - -[Goto `db/README.md` to view our database tables inside of Markdown](../../db/README.md) diff --git a/docs/local/DEV-COMMANDS.md b/docs/local/DEV-COMMANDS.md deleted file mode 100644 index 0e5503f19..000000000 --- a/docs/local/DEV-COMMANDS.md +++ /dev/null @@ -1,67 +0,0 @@ -> [!NOTE] -> Link to Justfile can be found [here](../../Justfile) - -# Shared - -`just dev` - Will run both the backend and frontend development server at the same time. - -`just devd` - Will run both the backend and frontend development server at the same time, but the backend will be in debug mode. See `just backend-dev-debug`. - -# Database - -`just drop` - Will drop your local database's public schema using the credentials provided in `.env` - -`just migrate` - Will migrate your local database using the credentials provided in `.env` - -`just migrate-prod` - Will manually migrate production database using the credentials provided in `.env.production` - -> [!WARNING] -> Should be used sparingly - -`just copy-stg` - Will pull credentials from `infra/.env.production` and do the following: - -1. Drop all tables from staging db -2. Copy all data from production db to staging db -3. Scrub/sanitize all records/tables that should not be allowed in staging db. - > [!WARNING] - > Should be used sparingly - -# Frontend - -`just dev` - Will run both the backend and frontend development server at the same time. - -`just frontend-install` - Download any missing frontend dependencies. An alias for `cd js && pnpm i`. - -`just frontend-dev` - Will only start the frontend Vite dev server. - -`just types-gen` - Regenerate the `schema.ts` file. - -> [!WARNING] -> The backend must be running - -`just frontend-test` - Run the entire frontend test suite - linters, autoformatters, typechecking, etc - -# Backend - -`just backend-install` - Builds and installs Spring backend. An alias for `./mvnw install -DskipTests=true`. - -`just backend-dev` - Will only start the backend Spring dev server. - -`just backend-dev-debug` - Will only start the backend Spring dev server, but will wait for a JVM debugger to attach to port 5005 first. - -`just backend-test` - Run Checkstyle and then the full test suite. - -`just backend-coverage` - Runs `just backend-test` and opens up the JaCoCo test coverage page in your default browser. - -`just backend-spotless` - Runs the backend formatter (currently Spotless with Palantir Java Formatter) and indicates whether or not you need to run the formatter on any files. - -`just backend-spotless-fix` - Runs the backend formatter (currently Spotless with Palantir Java Formatter) and will write to any files that have not been formatted yet. - -# React Email - -`just email-dev` - Run the React Email dev server. - -`just email-gen` - Will run `email/email.sh` which will build all React Email components into HTML and pass them into the backend's static folder. - -> [!WARNING] -> This is mainly used in development. diff --git a/docs/local/README.md b/docs/local/README.md deleted file mode 100644 index e30c998ca..000000000 --- a/docs/local/README.md +++ /dev/null @@ -1,8 +0,0 @@ -# Local development - -Please go through the docs in the given order - -1. [Setup](./SETUP.md) -1. [Developer Commands](./DEV-COMMANDS.md) -1. [Secrets](./SECRET.md) -1. [Testing API Endpoints](./TESTING-API-ENDPOINTS.md) diff --git a/docs/local/SECRET.md b/docs/local/SECRET.md deleted file mode 100644 index ba8c711c2..000000000 --- a/docs/local/SECRET.md +++ /dev/null @@ -1,19 +0,0 @@ -# Secrets - -Local environment files come from Azure. Git does not store them. - -[Setup](./SETUP.md) covers the rest of local setup. - -These files stay on your machine: - -- `.env` -- `.env.shared` -- `.env.ci` -- `.env.ci-app` -- `.env.staging` -- `.env.production` -- `.env.production-ro` -- `.env.migrator` -- `infra/.env.production` -- `internal/.env.ci` -- `internal/standup-bot/.env.production` diff --git a/docs/local/SETUP.md b/docs/local/SETUP.md deleted file mode 100644 index 618eb1ee5..000000000 --- a/docs/local/SETUP.md +++ /dev/null @@ -1,201 +0,0 @@ -# Prerequisites - -The following general software needs to be installed on your local machine: - -1. `JDK 25` - We use `openjdk`, but feel free to use `coretto` or any other distribution if you would like. -1. `maven` - Package manager to manage all our Java dependencies -1. `just` - The runner for `Justfiles`, which we use to consolidate our run commands. -1. `dotenvx` - Used to load environment variables from the root `.env` file. -1. `node` - Javascript runtime to run our frontend TypeScript code. -1. `corepack` - A package manager for package managers (???) to help us set a consistent `pnpm` version across all devs. -1. `pnpm@9` - Package manager that works faster than the default npm package manager. - -## MacOS - -The following instructions are using `homebrew` ([install instructions here](https://brew.sh/)), but it is not a requirement; you can follow along by installing all packages manually (though we would recommend against it). - -1. Install `openjdk@25` (aliased to `openjdk`): - - ```bash - brew install openjdk - ``` - -1. Install `maven`: - - ```bash - brew install maven - ``` - -1. Install `node`: - - ``` - brew install node - ``` - -1. You must then setup `corepack`. You can follow the instructions [here](https://github.com/nodejs/corepack?tab=readme-ov-file#how-to-install) under `Install Corepack using npm` on how to install and setup `corepack`. Once setup, simply enable `pnpm` on `corepack` like so: - - ``` - corepack enable pnpm - ``` - -1. Install `dotenvx`: - - ```bash - brew install dotenvx/brew/dotenvx - ``` - -1. Install `just`: - - ```bash - brew install just - ``` - -## Windows - -Unfortunately, I don't have a Windows machine that I develop on anymore, so I am unable to provide solid instructions for setup. However, you should be able to follow the exact same directions for [MacOS](#macos) but with `WinGet`/`Scoopy` or manually installing each software. - -# IDE Integration - -## VSCode - -> **NOTE**: If you open the codebloom repository in VSCode, it will prompt you to install recommended extensions to the workspace, which will include everything below. ->
image - -You need to install the following plugins: - -1. **EditorConfig** - Applies consistent spacing width and type across all editors -1. **Checkstyle for Java** - Java static analyzer -1. **Prettier** - Javascript formatter - - Helps maintain consistent styling - - Configure format on save [following these instructions](https://stackoverflow.com/questions/39494277/how-do-you-format-code-on-save-in-vs-code) -1. **ESLint** - Javascript linter - - Integrates with your project's ESLint configuration -1. **Babel JavaScript** - Improves JSX syntax highlighting -1. **Docker** - Provides Dockerfile IntelliSense -1. **DotENV** - `.env` file syntax highlighting -1. **Prettier Typescript Errors**: Simplifies complex TypeScript error messages -1. **Extension Pack for Java** - - Includes debuggers, formatters, and managers - - Supports format on save -1. **Spring Boot Extension Pack** - Additional Spring Boot-specific tooling -1. **Tailwind CSS IntelliSense** - Provides intelligent suggestions for Tailwind classes -1. **XML by RedHat** - - Official XML language support and formatter - - Important for editing Java XML files like pom.xml - -[.vscode/](https://github.com/tahminator/codebloom/tree/main/.vscode) defines some workspace defaults to help make development consistent. - -## IntelliJ - -You need to install the following plugins: - -1. **EditorConfig** - Applies consistent spacing width and type across all editors -1. **Checkstyle-IDEA** - Java static analyzer - -The Eclipse formatter and everything else should just work out of the box. You may need to install some plugins for TypeScript support, including Prettier, ESLint, Babel, Prettier, Tailwind, and more. - -## Neovim - -> **NOTE**: This may vary greatly by the current configuration of Neovim, but the following setup _should_ work out the box using `LazyVim`. - -You need to install the following plugins: - -1. **nvim-jdtls** - LSP for Java in Neovim (Install as a plugin, not `Mason`) - - `nvim-jdtls` may require some additional configuration. If it helps, my current config can be found [here](https://github.com/tahminator/dotfiles/blob/main/.config/nvim/lua/plugins/jdtls.lua) -1. **none-ls** - Provide a code bridge to formatting & LSP diagonostics. (Specifically used for Checkstyle formatting) -1. **vtsls** - LSP for TypeScript in Neovim (can install through `Mason`) -1. **eslint-lsp** - LSP Protocol for ESLint (can install through `Mason`) -1. **tailwindcss-language-server** - LSP for Tailwind (can install through `Mason`) -1. **json-lsp** - (Optional) LSP for JSON (can install through `Mason`) -1. **dockerfile-language-server** - (Optional) LSP for Dockerfile (can install through `Mason`) - -There is a [.lazy.lua](https://github.com/tahminator/codebloom/tree/main/.lazy.lua) file in the root directory that will apply some default, but only for `LazyVim`. Of course, you can replicate the behavior in your own distribution (and create a pull request with the changes). - -# Database - -## Postgres - -We currently use Postgres 16 locally to match production (but some members of the team have used 17 locally with no issues). - -### Installation - -You can feel free to download Postgres however you want, but the way we have all chose to set it up is with [Postgres.app](https://postgresapp.com) on MacOS. - -#### MacOS + Postgres.app - -1. Install `postgresapp` through `homebrew` (or the website if you would prefer to do so) - - ```bash - brew install --cask postgres-unofficial - ``` - -2. Open `Postgres.app` and click "Initialize" to create a new server. You are now ready to go! - - > - For `Postgres.app` instances, the password you enter to access the database doesn't matter - > - On first connection attempt, Postgres.app will prompt you to trust the specific program - > - After allowing access once, it won't ask again for that program - -3. Configure your `$PATH` to use the included command line tools (optional): - - ```bash - sudo mkdir -p /etc/paths.d && - echo /Applications/Postgres.app/Contents/Versions/latest/bin | sudo tee /etc/paths.d/postgresapp - ``` - -#### Other - -You may install it with Docker, or directly through [postgresql.com](https://postgresql.com) if you would like. While there are no directions I can directly offer to you, there are some very good tutorials online on how to do so. - -If you would like to use Docker, I can refer you to Patina's documentation for setting up Docker which you can find [here](https://github.com/arklian/patina/blob/main/docs/postgres-on-docker.md) - -### Viewer - -You can feel free to use any viewer you want, but we would recommend [DataGrip](https://www.jetbrains.com/datagrip/) which is free for all non-commercial use. - -## Redis - -We currently use Redis 7 in production, but feel free to use a different version (although you may run into some compatibility issues). - -### Installation - -You can feel free to download Redis however you want, but the way we have all chose to set it up is with Homebrew. - -#### Homebrew - -1. Install `redis` through `homebrew` - - ```bash - brew install redis - ``` - -2. To launch the redis server, you can simply run the following command to run the server: - - ```bash - redis-server - ``` - - > **NOTE**: This will launch the server in the foreground (when the terminal is closed, the Redis server will be closed). - > - > If you would like to automatically launch the server in the background (as soon as your machine is on and you are logged in), you can run this command: - - ```bash - brew services start redis - ``` - -3. The default Redis URI for Homebrew installations is simply: - - ```bash - redis://localhost:6379/0 - ``` - - > **NOTE**: By default, Redis has 16 databases (the number after the slash) from 0-15. - -### Viewer - -You can feel free to use any viewer you want, but we would recommend [DataGrip](https://www.jetbrains.com/datagrip/) which is free for all non-commercial use. - -# Secrets - -Local `.env` comes from Azure. - -If there is a key specific to an environment (such as `CI` or `staging` environment), please consult the tech docs within the `CI` group. diff --git a/docs/local/TESTING-API-ENDPOINTS.md b/docs/local/TESTING-API-ENDPOINTS.md deleted file mode 100644 index ae42c31b0..000000000 --- a/docs/local/TESTING-API-ENDPOINTS.md +++ /dev/null @@ -1,65 +0,0 @@ -# Prerequisites - -1. Postman - -# How-to test any new API endpoints with Postman - -1. Ensure that you deployed your changes to staging within your PR ([read more here](https://github.com/tahminator/codebloom/wiki/GitHub-Actions#how-to-trigger-deployment)). - -1. Inside of Postman, hit the Import button near the top left: - - image - -1. Inside the input box, enter the following link: - - ``` - https://stg.codebloom.patinanetwork.org/v3/api-docs - - # or if you're testing things locally - http://localhost:8080/v3/api-docs - - # or prod - https://codebloom.patinanetwork.org/v3/api-docs - ``` - - image - -1. Once it finishes importing the OpenAPI spec, hit `View Import Settings` and set `Folder organization` to `Tags`: - - image - image - -1. You can then hit `Import` and rename the new collection to indicate the environment (e.g. `Codebloom dev` or `Codebloom stg`) - - image - -# How to authenticate requests in Postman - -> [!NOTE] -> These instructions require Google Chrome (or any Chromium based browser) - -## Prerequisites - -1. Install Postman Interceptor [here](https://chromewebstore.google.com/detail/postman-interceptor/aicmkgpgakddgnaphhhpliifpcfhicfo?hl=en) - -## Instructions - -1. Log into Postman Interceptor with the same account that you used to authenticate on your local Postman instance. - -1. Swap to the `Sync Cookie` tab - - image - -1. Enter in `stg.codebloom.patinanetwork.org` (or local/prod) into the domain list - - image - -1. Hit `Sync Cookies` - - image - -1. Navigate to [stg.codebloom.patinanetwork.org](https://stg.codebloom.patinanetwork.org) (or local/prod) and log in as you normally would (if you are logged in, log out then log back in). - -1. Once authenticated, Postman Interceptor will automatically copy your authentication cookie to your local Postman client, and you can now hit endpoints under your user! - - image diff --git a/docs/local/WORKFLOW.md b/docs/local/WORKFLOW.md deleted file mode 100644 index e74fba2e4..000000000 --- a/docs/local/WORKFLOW.md +++ /dev/null @@ -1,30 +0,0 @@ -# Feature Workflow - -Use this flow when handling a feature from assignment through merge and handoff. - -## Workflow - -1. Read the task description in Notion and understand the requested change/feature. -2. Set the Notion ticket status to `In Progress`. -3. Pull the latest `main` branch. -4. Create a local branch named exactly the Notion ticket ID. -5. Make every commit message start with `: `. -6. Open a pull request when the change/feature is completed. -7. Set the Notion ticket status to `Pending PR`. -8. Add a clear PR description. -9. Add a screenshot from the development environment (if available). -10. Deploy the feature to staging (type /deploy in the PR comments). -11. Add a screenshot from the staging environment (if available). -12. Ensure all PR checklist items are completed. -13. Resolve any Copilot suggestions. -14. Ping a reviewer on discord. -15. Resolve reviewer comments. -16. Rebase and merge the branch into `main`. -17. Set the Notion ticket status to `Done`. - -## Notes - -- Keep the branch name and commit prefix aligned with the same ticket ID. -- If screenshots are not available, note that in the PR description. -- Try to squash commits when possible. - - Example: If you have a commit like "checkstyle fix", squash it into the most relevant feature commit. \ No newline at end of file diff --git a/docs/observability/README.md b/docs/observability/README.md deleted file mode 100644 index aead236ca..000000000 --- a/docs/observability/README.md +++ /dev/null @@ -1,74 +0,0 @@ -# Observability & Metrics - -Grafana - -Codebloom uses Spring Boot Actuator and Prometheus to provide operational insights into the running application. We also collect logs via OpenSearch, which is setup through the DigitalOcean App Platform [(view app spec here)](../../.do/specs.ts). Both of these data sources are then fed to a Grafana instance hosted on [monitor.tahmid.io](https://monitor.tahmid.io) - -## Grafana - -All metrics for production & staging can be viewed on [monitor.tahmid.io](https://monitor.tahmid.io). - -> [!NOTE] -> Please reach out to [@tahminator](https://github.com/tahminator) if you are on the Codebloom dev team & need the credentials to login & view the dashboard - -## Actuator Endpoints - -Spring Boot Actuator exposes operational information about the running application through HTTP endpoints. - -### Security - -All actuator endpoints are protected with HTTP Basic Authentication to prevent unauthorized access to sensitive operational data. - -**Authentication Details:** - -- **Authentication Type:** HTTP Basic Auth -- **Role Required:** `ACTUATOR` -- **Credentials:** Stored in environment variables - - `ACTUATOR_USERNAME` - Username for actuator endpoints - - `ACTUATOR_PASSWORD` - Password for actuator endpoints (generate with `openssl rand -base64 48 | head -c 64`) - -### Available Endpoints - -Currently exposed endpoints: - -- **`/actuator/prometheus`** - Prometheus-formatted metrics endpoint for scraping - -## Prometheus Metrics - -Prometheus metrics provide detailed insights into application performance, JVM statistics, HTTP requests, and custom business metrics. - -### Accessing Metrics - -**Local Development:** - -``` -http://localhost:8080/actuator/prometheus -``` - -(Requires HTTP Basic Auth with actuator credentials) - -**Staging:** - -``` -https://stg.codebloom.patinanetwork.org/actuator/prometheus -``` - -**Production:** - -``` -https://codebloom.patinanetwork.org/actuator/prometheus -``` - -### Testing Locally - -To test the actuator endpoint locally: - -1. Ensure your `.env` file has `ACTUATOR_USERNAME` and `ACTUATOR_PASSWORD` set -2. Start the application (`just dev`) -3. Access the endpoint using curl: - -```bash -curl -u actuator:your_password http://localhost:8080/actuator/prometheus -``` - -Or use a browser and enter the username/password when prompted. diff --git a/docs/observability/dashboard.png b/docs/observability/dashboard.png deleted file mode 100644 index eecb1c627..000000000 Binary files a/docs/observability/dashboard.png and /dev/null differ diff --git a/e2e/README.md b/e2e/README.md index 1e4ac3f7e..8d6da743b 100644 --- a/e2e/README.md +++ b/e2e/README.md @@ -9,7 +9,7 @@ These tests run daily at 12:18 ET every day under [.github/scripts/test/run-e2e- ## Requirements -- `pnpm` - Follow the main developer installation instructions under [docs/local/SETUP.md](../docs/local/SETUP.md). +- `pnpm` - Follow the main developer installation instructions under [local setup](https://docs.patinanetwork.org/codebloom/local/SETUP/). ## Run diff --git a/email/README.md b/email/README.md index f243cf4ac..377471e54 100644 --- a/email/README.md +++ b/email/README.md @@ -7,7 +7,7 @@ These templates are then built into `*.html` files which are then loaded at runt ### Requirements - [pnpm](https://pnpm.io/) - > It is **STRONGLY** recommended that you follow the setup directions for `pnpm` inside of [docs/setup/README.md](../../docs/setup/README.md) + > It is **STRONGLY** recommended that you follow the setup directions for `pnpm` inside of [local setup](https://docs.patinanetwork.org/codebloom/local/SETUP/) ### Run diff --git a/src/README.md b/src/README.md index 2573cf3e0..a035fccf7 100644 --- a/src/README.md +++ b/src/README.md @@ -14,7 +14,7 @@ View [STYLEGUIDE.md](../STYLEGUIDE.md) to view best practices. ## Requirements > [!WARNING] -> It is HIGHLY recommended that you follow the setup instructions at [docs/local/README.md](../docs/local/README.md) to get up and running. +> It is HIGHLY recommended that you follow the setup instructions at [local development docs](https://docs.patinanetwork.org/codebloom/local/) to get up and running. - [`openjdk@25`](https://openjdk.org/projects/jdk/25/) (though you may feel free to use any other distribution if you would like) - [`mvn`](https://maven.apache.org/what-is-maven.html) - you do not need to explicitly download this, just use `./mvnw` from the root directory instead (our `Justfile` commands all use `./mvnw` as well). @@ -22,7 +22,7 @@ View [STYLEGUIDE.md](../STYLEGUIDE.md) to view best practices. ## Run > [!WARNING] -> It is HIGHLY recommended that you follow the setup instructions at [docs/local/README.md](../docs/local/README.md) to get up and running. +> It is HIGHLY recommended that you follow the setup instructions at [local development docs](https://docs.patinanetwork.org/codebloom/local/) to get up and running. ## Structure diff --git a/src/main/java/org/patinanetwork/codebloom/utilities/generator/complex/ComplexJSTypesGenerator.java b/src/main/java/org/patinanetwork/codebloom/utilities/generator/complex/ComplexJSTypesGenerator.java index 0680bf28a..bde3d687c 100644 --- a/src/main/java/org/patinanetwork/codebloom/utilities/generator/complex/ComplexJSTypesGenerator.java +++ b/src/main/java/org/patinanetwork/codebloom/utilities/generator/complex/ComplexJSTypesGenerator.java @@ -3,13 +3,17 @@ import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.databind.ObjectMapper; import com.google.common.annotations.VisibleForTesting; +import java.io.File; +import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.util.HashSet; import java.util.List; +import java.util.Locale; import java.util.Map; import java.util.Set; +import java.util.regex.Pattern; import lombok.extern.slf4j.Slf4j; import org.patinanetwork.codebloom.shared.tag.ParentTags; import org.patinanetwork.codebloom.shared.tag.TagMetadataList; @@ -208,8 +212,11 @@ private void writeToFile() throws Exception { Files.createDirectories(outputPath.getParent()); Files.writeString(outputPath, finalOutput.toString()); - Path prettierBin = Paths.get("js/node_modules/.bin/prettier").toAbsolutePath(); + Path nodeExecutable = resolveNodeExecutable(); + Path prettierBin = + Paths.get("js/node_modules/prettier/bin/prettier.cjs").toAbsolutePath(); new ProcessBuilder( + nodeExecutable.toString(), prettierBin.toString(), "--write", outputPath.toAbsolutePath().toString()) @@ -219,4 +226,35 @@ private void writeToFile() throws Exception { log.info("Generated TypeScript file: {} with {} import(s)", outputPath, imports.size()); } + + private static Path resolveNodeExecutable() throws IOException { + boolean windows = + System.getProperty("os.name", "").toLowerCase(Locale.ROOT).contains("win"); + String[] executableNames = windows ? new String[] {"node.exe", "node"} : new String[] {"node"}; + return resolveExecutableOnPath(System.getenv("PATH"), executableNames); + } + + @VisibleForTesting + static Path resolveExecutableOnPath(String pathEnv, String... executableNames) throws IOException { + if (pathEnv == null || pathEnv.isBlank()) { + throw new IOException("PATH is unset, so node cannot be located"); + } + for (String directory : pathEnv.split(Pattern.quote(File.pathSeparator))) { + String trimmed = directory.trim(); + if (trimmed.isEmpty()) { + continue; + } + Path directoryPath = Paths.get(trimmed); + if (!directoryPath.isAbsolute()) { + continue; + } + for (String executableName : executableNames) { + Path candidate = directoryPath.resolve(executableName); + if (Files.isRegularFile(candidate) && Files.isExecutable(candidate)) { + return candidate.toRealPath(); + } + } + } + throw new IOException("node was not found in an absolute directory on PATH"); + } } diff --git a/src/test/java/org/patinanetwork/codebloom/utilities/generator/complex/ComplexJSTypesGeneratorTest.java b/src/test/java/org/patinanetwork/codebloom/utilities/generator/complex/ComplexJSTypesGeneratorTest.java index 4fa3bbe08..f20d7f935 100644 --- a/src/test/java/org/patinanetwork/codebloom/utilities/generator/complex/ComplexJSTypesGeneratorTest.java +++ b/src/test/java/org/patinanetwork/codebloom/utilities/generator/complex/ComplexJSTypesGeneratorTest.java @@ -1,10 +1,19 @@ package org.patinanetwork.codebloom.utilities.generator.complex; +import static java.nio.file.attribute.PosixFilePermission.OWNER_EXECUTE; +import static java.nio.file.attribute.PosixFilePermission.OWNER_READ; +import static java.nio.file.attribute.PosixFilePermission.OWNER_WRITE; import static org.junit.jupiter.api.Assertions.*; +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; import java.util.LinkedHashMap; import java.util.Map; +import java.util.Set; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; import org.patinanetwork.codebloom.common.db.models.question.topic.LeetcodeTopicEnum; import org.patinanetwork.codebloom.common.db.models.usertag.Tag; import org.patinanetwork.codebloom.shared.tag.TagMetadataList; @@ -111,4 +120,29 @@ void topicMetadataListAllHaveNonEmptyName() { assertFalse(metadata.getName().isBlank()); } } + + @Test + void resolveExecutableOnPathReturnsAbsoluteExecutable(@TempDir Path root) throws Exception { + Path blocked = root.resolve("blocked"); + Path allowed = root.resolve("allowed"); + Files.createDirectories(blocked); + Files.createDirectories(allowed); + Path blockedNode = blocked.resolve("node"); + Path allowedNode = allowed.resolve("node"); + Files.writeString(blockedNode, ""); + Files.writeString(allowedNode, ""); + Files.setPosixFilePermissions(blockedNode, Set.of(OWNER_READ, OWNER_WRITE)); + Files.setPosixFilePermissions(allowedNode, Set.of(OWNER_READ, OWNER_EXECUTE)); + + String pathEnv = blocked + File.pathSeparator + "relative-bin" + File.pathSeparator + allowed; + + assertEquals(allowedNode.toRealPath(), ComplexJSTypesGenerator.resolveExecutableOnPath(pathEnv, "node")); + } + + @Test + void resolveExecutableOnPathRejectsRelativeOrMissingEntries() { + assertThrows(IOException.class, () -> ComplexJSTypesGenerator.resolveExecutableOnPath(null, "node")); + assertThrows(IOException.class, () -> ComplexJSTypesGenerator.resolveExecutableOnPath(" ", "node")); + assertThrows(IOException.class, () -> ComplexJSTypesGenerator.resolveExecutableOnPath("relative-bin", "node")); + } }