Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 0 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ uv.lock
.mypy_cache
.dmypy.json
.pytest_cache
.yarn

*.env
!ci.env
Expand All @@ -15,7 +14,6 @@ node_modules
coverage.xml
coverage.json

backend/yarn.lock
backend/static

/process-compose.yml
4 changes: 0 additions & 4 deletions backend/yarn.lock

This file was deleted.

2 changes: 0 additions & 2 deletions docs/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -18,5 +18,3 @@ next-env.d.ts
_pagefind/

npm-debug.log*
yarn-debug.log*
yarn-error.log*
2 changes: 1 addition & 1 deletion docs/content/community/development.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ The database URL can be specified per environment in the `.env` files (see

## Running the frontend and backend

To run Bracket (frontend and backend) locally without Docker, one needs `yarn` and `uv`.
To run Bracket (frontend and backend) locally without Docker, one needs `pnpm` and `uv`.

The following starts the frontend and backend for local development in the root
directory of Bracket:
Expand Down
42 changes: 41 additions & 1 deletion docs/content/deployment/cloud-services.mdx
Original file line number Diff line number Diff line change
@@ -1,8 +1,34 @@
---
title: Cloud services
---
import {Callout} from "nextra/components"

# Cloud services

The frontend of Bracket is powered by Vite and can be simply statically hosted on any cloud
service, such as Vercel or Cloudflare. Vite's [documentation](https://vite.dev/guide/static-deploy)
provides detailed instructions on how to deploy your Vite application.

<Callout type="info">
Essentially, the only thing you (or a cloud provider) needs to do is to run `pnpm run build`
to generate a production build, and then serve the contents of the `dist` directory to the internet.
</Callout>

## Cloudflare

To deploy the frontend to Cloudflare, go to `Workers & Pages` and click on
`Import an existing Git repository`.

Make sure to:

- Select `React (Vite)` as framework
- Select the `frontend` directory as root directory

## GitHub Pages

To deploy the frontend to GitHub Pages, follow the steps outlined in
Vite's [docs](https://vite.dev/guide/static-deploy#github-pages).

## Vercel

To deploy the frontend to Vercel, use the following link:
Expand All @@ -11,4 +37,18 @@ To deploy the frontend to Vercel, use the following link:
https://vercel.com/new/project?template=https://github.com/evroon/bracket
```

Make sure to select the `frontend` directory as root directory, and use Next.js as framework.
Make sure to:

- Select `Vite` as framework
- Select the `frontend` directory as root directory

This should work. If it fails, Vercel didn't automatically detect the right build settings.
Change the following settings under `Build and Output settings`:

- Set the build command to `pnpm run build`
- Set the install command to `pnpm install`

## Other cloud providers

Vite's [documentation](https://vite.dev/guide/static-deploy)
provides detailed instructions on how to deploy your Vite application.
4 changes: 2 additions & 2 deletions docs/content/deployment/systemd.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ This section describes how to deploy Bracket (frontend and backend) as a Systemd

This assumes:

- You have installed `yarn` and `pipenv`.
- You have installed `pnpm` and `uv`.
- You have a PostgreSQL cluster running.
- You have cloned Bracket in `/var/lib/bracket`.
- You have created a new user called Bracket with the permissions to read
Expand All @@ -29,7 +29,7 @@ After=network.target
Type=simple
User=bracket
WorkingDirectory=/var/lib/bracket/backend
ExecStart=pipenv run gunicorn -k uvicorn.workers.UvicornWorker bracket.app:app --bind localhost:8400 --workers 1
ExecStart=uv run gunicorn -k uvicorn.workers.UvicornWorker bracket.app:app --bind localhost:8400 --workers 1
Environment=ENVIRONMENT=PRODUCTION
TimeoutSec=15
Restart=always
Expand Down
2 changes: 1 addition & 1 deletion docs/content/running-bracket/configuration.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ Copy `ci.env` to `prod.env` and fill in the values:
- `ALLOW_INSECURE_HTTP_SSO`: Should not be used in production. Allows use of INSECURE requests for
SSO auth.
- `AUTO_RUN_MIGRATIONS`: Whether to run (alembic) migrations automatically on startup or not.
Migrations can be applied manually using `pipenv run alembic upgrade head`.
Migrations can be applied manually using `uv run alembic upgrade head`.

### Backend: Example configuration file

Expand Down
2 changes: 1 addition & 1 deletion docs/content/running-bracket/quickstart.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -22,5 +22,5 @@ be able to view bracket at `http://localhost:3000`. You can log in with the foll
To insert dummy rows into the database, run:

```bash
sudo docker exec bracket-backend pipenv run ./cli.py create-dev-db
sudo docker exec bracket-backend uv run ./cli.py create-dev-db
```
2 changes: 1 addition & 1 deletion docs/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
"start": "next start",
"prettier:check": "prettier --check \"**/*.{js,jsx,ts,tsx}\"",
"prettier:write": "prettier --write \"**/*.{js,jsx,ts,tsx}\"",
"test": "pnpm run prettier:write && pnpm markdownlint-cli2 --fix",
"test": "pnpm run prettier:write && pnpm markdownlint-cli2 --fix --config .markdownlint-cli2.mjs",
"test-check": "pnpm run prettier:check && pnpm markdownlint-cli2 --config .markdownlint-cli2.mjs",
"lint:markdown": "markdownlint-cli2",
"postbuild": "pagefind --site .next/server/app --output-path out/_pagefind && next-sitemap"
Expand Down
2 changes: 0 additions & 2 deletions frontend/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,6 @@

# debug
npm-debug.log*
yarn-debug.log*
yarn-error.log*

# local env files
.env.local
Expand Down
5 changes: 5 additions & 0 deletions frontend/Caddyfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
:3000 {
root * /app/dist
file_server
try_files {path} /index.html
}
40 changes: 8 additions & 32 deletions frontend/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,47 +1,23 @@
# Install dependencies only when needed
FROM node:22-alpine AS deps

WORKDIR /app

COPY pnpm-lock.yaml package.json ./

RUN corepack enable && pnpm i

# Rebuild the source code only when needed
# Build static files
FROM node:22-alpine AS builder

WORKDIR /app

COPY . .
COPY --from=deps /app/node_modules ./node_modules
ENV NODE_ENV=production

RUN corepack enable
COPY . .

RUN VITE_API_BASE_URL=http://VITE_API_BASE_URL_PLACEHOLDER \
VITE_HCAPTCHA_SITE_KEY=VITE_HCAPTCHA_SITE_KEY_PLACEHOLDER \
pnpm build
RUN corepack enable && pnpm install && pnpm build

# Production image, copy all the files and run next
FROM node:22-alpine AS runner
# Production image, copy all the static files and run next
FROM caddy:2-alpine AS runner

WORKDIR /app

ENV NODE_ENV=production

RUN addgroup -g 1001 --system nodejs && \
adduser --system vite -u 1001 -G nodejs

COPY --from=builder --chown=vite:nodejs /app/public ./public
COPY --from=builder /app/node_modules ./node_modules
COPY --from=builder /app/package.json ./package.json

RUN apk add bash

USER vite
COPY --from=builder /app/dist /app/dist
COPY --from=builder /app/Caddyfile /etc/caddy/Caddyfile

EXPOSE 3000

HEALTHCHECK --interval=10s --timeout=5s --retries=5 \
CMD ["wget", "--spider", "http://0.0.0.0:3000", "||", "exit", "1"]

CMD ["pnpm", "start"]
21 changes: 0 additions & 21 deletions frontend/LICENCE

This file was deleted.

9 changes: 7 additions & 2 deletions frontend/src/components/utils/error_alert.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Alert } from '@mantine/core';
import { Alert, Center } from '@mantine/core';
import { IconAlertCircle } from '@tabler/icons-react';
import React from 'react';

Expand All @@ -10,6 +10,7 @@ export function ErrorAlert({ title, message }: { title: string; message: string
color="red"
radius="lg"
variant="outline"
w="40rem"
>
{message}
</Alert>
Expand All @@ -23,5 +24,9 @@ export default function RequestErrorAlert({ error }: any) {
: 'Error';
const message = `${status_code}: ${error.response ? error.response.data.detail : error.message}`;

return <ErrorAlert message={message} title="Error" />;
return (
<Center>
<ErrorAlert message={message} title="Error" />
</Center>
);
}
7 changes: 6 additions & 1 deletion frontend/src/pages/404.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { Button, Container, Group, Text, Title } from '@mantine/core';
import { useTranslation } from 'react-i18next';
import { useNavigate } from 'react-router';

import { tokenPresent } from '../services/local_storage';
import classes from './404.module.css';

export default function NotFoundPage() {
Expand All @@ -16,7 +17,11 @@ export default function NotFoundPage() {
{t('not_found_description')}
</Text>
<Group justify="center">
<Button variant="subtle" size="md" onClick={() => navigate('/')}>
<Button
variant="subtle"
size="md"
onClick={() => (tokenPresent() ? navigate('/') : navigate('/login'))}
>
{t('back_home_nav')}
</Button>
</Group>
Expand Down
3 changes: 3 additions & 0 deletions frontend/src/pages/user.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import { Group, Stack, Title } from '@mantine/core';
import React from 'react';
import { useTranslation } from 'react-i18next';

import UserForm from '../components/forms/user';
import RequestErrorAlert from '../components/utils/error_alert';
import { TableSkeletonSingleColumn } from '../components/utils/skeletons';
import { checkForAuthError, getUser } from '../services/adapter';
import Layout from './_layout';
Expand All @@ -26,6 +28,7 @@ export default function UserPage() {
return (
<Layout>
<Title>{t('edit_profile_title')}</Title>
{swrUserResponse.error && <RequestErrorAlert error={swrUserResponse.error} />}
<Stack style={{ maxWidth: '40rem' }}>{content}</Stack>
</Layout>
);
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/services/local_storage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ export function performLogoutAndRedirect(t: Translator, navigate: NavigateFuncti
message: '',
autoClose: 10000,
});
navigate('/login');
navigate('/login', { replace: true });
}

export function getLogin() {
Expand Down
6 changes: 3 additions & 3 deletions process-compose-example.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ log_level: debug
processes:
frontend:
working_dir: "frontend"
command: "yarn run dev"
command: "pnpm run dev"
availability:
restart: "on_failure"
readiness_probe:
Expand All @@ -19,7 +19,7 @@ processes:

backend:
working_dir: "backend"
command: "pipenv run gunicorn -k bracket.uvicorn.RestartableUvicornWorker bracket.app:app --bind localhost:8400 --workers 1 --reload"
command: "uv run gunicorn -k bracket.uvicorn.RestartableUvicornWorker bracket.app:app --bind localhost:8400 --workers 1 --reload"
availability:
restart: "on_failure"
environment:
Expand All @@ -36,7 +36,7 @@ processes:

docs:
working_dir: "docs"
command: "yarn run dev -p 3001"
command: "pnpm run dev -p 3001"
availability:
restart: "on_failure"
readiness_probe:
Expand Down