Skip to content

Commit 91f865d

Browse files
Merge branch 'feat-profiles' of https://github.com/appwrite/console into fix-code-editor-issues
2 parents b60ab5e + 335c6d4 commit 91f865d

File tree

371 files changed

+9961
-10436
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

371 files changed

+9961
-10436
lines changed

.env.example

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,5 @@ PUBLIC_STRIPE_KEY=
77
PUBLIC_GROWTH_ENDPOINT=
88
PUBLIC_CONSOLE_EMAIL_VERIFICATION=false
99
PUBLIC_CONSOLE_MOCK_AI_SUGGESTIONS=true
10-
PUBLIC_AI_SERVICE_BASE_URL=
10+
PUBLIC_AI_SERVICE_BASE_URL=
11+
PUBLIC_IMAGINE_CDN_URL=
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
name: Copilot Setup Steps
2+
3+
# Automatically run the setup steps when they are changed to allow for easy validation, and
4+
# allow manual testing through the repository's "Actions" tab
5+
on:
6+
workflow_dispatch:
7+
push:
8+
paths:
9+
- .github/workflows/copilot-setup-steps.yml
10+
pull_request:
11+
paths:
12+
- .github/workflows/copilot-setup-steps.yml
13+
14+
jobs:
15+
copilot-setup-steps:
16+
runs-on: ubuntu-latest
17+
permissions:
18+
contents: read
19+
steps:
20+
- uses: actions/checkout@v5
21+
- name: Use Node.js
22+
uses: actions/setup-node@v6
23+
with:
24+
node-version: 20
25+
- name: Install pnpm
26+
uses: pnpm/action-setup@v4
27+
- name: Install dependencies
28+
run: pnpm install --frozen-lockfile
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
name: Dockerize Profiles
2+
3+
on:
4+
push:
5+
branches: [feat-profiles]
6+
pull_request:
7+
types: [opened, synchronize, reopened]
8+
branches: [feat-profiles]
9+
workflow_dispatch:
10+
11+
jobs:
12+
dockerize-profiles:
13+
runs-on: ubuntu-latest
14+
15+
steps:
16+
- name: Checkout the repo
17+
uses: actions/checkout@v2
18+
- name: Set up QEMU
19+
uses: docker/setup-qemu-action@v2
20+
- name: Set up Docker Buildx
21+
uses: docker/setup-buildx-action@v2
22+
- name: Log in to Docker Hub
23+
uses: docker/login-action@v3
24+
with:
25+
username: ${{ vars.DOCKERHUB_USERNAME }}
26+
password: ${{ secrets.DOCKERHUB_TOKEN }}
27+
28+
- name: Extract metadata (tags, labels) for Docker
29+
id: meta
30+
uses: docker/metadata-action@v5
31+
with:
32+
images: appwrite/console-profiles
33+
tags: |
34+
type=ref,event=branch,prefix=branch-
35+
type=ref,event=pr
36+
type=sha,prefix=sha-
37+
type=raw,value=gh-${{ github.run_id}}
38+
flavor: |
39+
latest=false
40+
41+
- name: Build and push Docker image
42+
id: push
43+
uses: docker/build-push-action@v6
44+
with:
45+
context: .
46+
push: true
47+
platforms: linux/amd64,linux/arm64
48+
tags: ${{ steps.meta.outputs.tags }}
49+
labels: ${{ steps.meta.outputs.labels }}

.github/workflows/publish.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ jobs:
8383
"PUBLIC_CONSOLE_MODE=cloud"
8484
"PUBLIC_CONSOLE_FEATURE_FLAGS="
8585
"PUBLIC_APPWRITE_MULTI_REGION=true"
86-
"PUBLIC_CONSOLE_EMAIL_VERIFICATION=false"
86+
"PUBLIC_CONSOLE_EMAIL_VERIFICATION=true"
8787
"PUBLIC_CONSOLE_MOCK_AI_SUGGESTIONS=false"
8888
"PUBLIC_GROWTH_ENDPOINT=${{ secrets.PUBLIC_GROWTH_ENDPOINT }}"
8989
"PUBLIC_STRIPE_KEY=${{ secrets.PUBLIC_STRIPE_KEY_STAGE }}"

.github/workflows/tests.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,9 @@ on:
88
- 'static/**/*'
99

1010
env:
11+
PUBLIC_CONSOLE_PROFILE: 'default'
1112
PUBLIC_APPWRITE_ENDPOINT: http://appwrite.test/v1
13+
PUBLIC_AI_SERVICE_BASE_URL: 'http://appwrite.test/v1'
1214

1315
jobs:
1416
build:

AGENTS.md

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
# Appwrite Console - Copilot Instructions
2+
3+
## Repository Overview
4+
5+
Appwrite Console is the web-based GUI for the Appwrite backend-as-a-service platform. Single-page application built with **Svelte 5 + SvelteKit 2**, **TypeScript** (not strict mode), **Vite 7**, tested with **Vitest + Playwright**. Package manager: **pnpm 10.15.1**, Node 20+. ~1500 files with extensive component-based architecture.
6+
7+
## Critical Build & Test Commands
8+
9+
### Setup (REQUIRED before any commands)
10+
11+
1. **Install pnpm**: `npm install -g corepack && corepack enable && corepack prepare pnpm@10.15.1 --activate`
12+
2. **Create .env**: `cp .env.example .env` (configure `PUBLIC_APPWRITE_ENDPOINT` and `PUBLIC_CONSOLE_MODE`)
13+
3. **Configure network access** (if using GitHub Actions or restricted environments):
14+
- Ensure firewall/proxy allows access to: `pkg.pr.new`, `pkg.vc`, `registry.npmjs.org`
15+
- These domains are required for dependencies: `@appwrite.io/console`, `@appwrite.io/pink-icons-svelte`, `@appwrite.io/pink-svelte`
16+
- In GitHub Actions: Use `pnpm/action-setup@v4` which handles registry configuration
17+
- If network errors persist, check proxy settings: `npm config get proxy` and `npm config get https-proxy`
18+
4. **Install dependencies**: `pnpm install --frozen-lockfile` (if pkg.pr.new/pkg.vc fail due to network restrictions, installation may still succeed with cached versions)
19+
20+
### Development Commands
21+
22+
**Standard workflow**: `check``lint``test``build` (before committing)
23+
24+
- `pnpm run check` - TypeScript/Svelte validation (~30-60s)
25+
- `pnpm run lint` - ESLint check (~10-20s)
26+
- `pnpm run format` - Auto-fix Prettier formatting
27+
- `pnpm run test` - Vitest unit tests with TZ=EST (~10-30s)
28+
- `pnpm run build` - Production build via build.js (~60-120s)
29+
- `pnpm dev` - Dev server on port 3000
30+
- `pnpm run preview` - Preview build on port 4173
31+
- `pnpm run e2e` - Playwright tests (needs `pnpm exec playwright install --with-deps chromium` first, ~120s+)
32+
33+
**CI Pipeline** (`.github/workflows/tests.yml`): audit → install → check → lint → test → build
34+
35+
## Project Structure
36+
37+
```
38+
src/
39+
├── lib/ # Reusable logic ($lib alias)
40+
│ ├── components/ # Feature components (billing, domains, permissions, etc.)
41+
│ ├── elements/ # Basic UI elements
42+
│ ├── helpers/ # Utility functions (array, date, string, etc.)
43+
│ ├── stores/ # Svelte stores for state
44+
│ ├── sdk/ # Appwrite SDK wrappers
45+
│ └── constants.ts, flags.ts, system.ts
46+
├── routes/
47+
│ ├── (console)/ # Auth-required routes
48+
│ │ ├── organization-[organization]/
49+
│ │ └── project-[region]-[project]/ # databases, functions, messaging, storage
50+
│ └── (public)/ # Public routes (login, register, auth callbacks)
51+
├── themes/ # Theme definitions ($themes alias)
52+
└── app.html, hooks.{client,server}.ts, service-worker.ts
53+
```
54+
55+
**SvelteKit conventions**: `+page.svelte` (component), `+page.ts` (data loader), `+layout.svelte` (wrapper), `+error.svelte` (errors). Groups like `(console)` organize routes without affecting URLs. Dynamic params: `[param]`.
56+
57+
## Key Configuration
58+
59+
**svelte.config.js**: Adapter = static SPA (fallback: index.html), base path `/console`, aliases: `$lib`, `$routes`, `$themes`
60+
**vite.config.ts**: Dev port 3000, Vitest (client=jsdom, server=node), test files: `src/**/*.{test,spec}.{js,ts}`
61+
**tsconfig.json**: Extends `.svelte-kit/tsconfig.json`, **NOT strict mode** (`strict: false`)
62+
**eslint.config.js**: Flat config (ESLint 9+), many rules disabled (see TODOs)
63+
**.prettierrc**: 4 spaces, single quotes, 100 char width, no trailing commas
64+
65+
## Testing
66+
67+
**Unit (Vitest)**: Tests in `src/lib/helpers/*.test.ts`, run with `TZ=EST` (timezone matters). Setup mocks SvelteKit (`$app/*`) in `vitest-setup-client.ts`.
68+
**E2E (Playwright)**: Tests in `e2e/journeys/*.spec.ts`, needs build+preview on port 4173, retries 3x, timeout 120s, Chromium only.
69+
70+
## Common Pitfalls
71+
72+
1. **Blank page in dev**: Disable ad blockers if seeing "Failed to fetch dynamically imported module" (known SvelteKit issue)
73+
2. **Network errors on install**:
74+
- pkg.pr.new/pkg.vc deps may fail due to firewall/proxy restrictions
75+
- Check access: `curl -I https://pkg.pr.new` and `curl -I https://pkg.vc`
76+
- Configure proxy if needed: `npm config set proxy http://proxy:port` and `npm config set https-proxy http://proxy:port`
77+
- GitHub Actions: Ensure runner has internet access; use `pnpm/action-setup@v4` action
78+
- Local dev: Often safe to continue with cached versions if network fails
79+
3. **OOM on build**: Set `NODE_OPTIONS=--max_old_space_size=8192` (like Dockerfile does)
80+
4. **Test failures**: Always use `pnpm run test` (sets TZ=EST), not `vitest` directly
81+
5. **TS errors not showing**: Run `pnpm run check` explicitly (dev server doesn't always surface them)
82+
6. **Format vs lint conflicts**: Run `pnpm run format` before `pnpm run lint`
83+
7. **E2E timeouts**: Wait 120s for preview server startup, tests auto-retry 3x
84+
8. **Stale build**: Clear `.svelte-kit` if changes not reflected: `rm -rf .svelte-kit && pnpm run build`
85+
86+
## Code Conventions
87+
88+
- Imports: Use `$lib`, `$routes`, `$themes` aliases
89+
- Components: PascalCase, in `src/lib/components/[feature]/`
90+
- Helpers: Pure functions in `src/lib/helpers/`
91+
- Types: Inline or `.d.ts`, not `.types.ts` files
92+
- Comments: Minimal, use for TODOs or complex logic
93+
- TypeScript: Not strict mode, `any` tolerated
94+
95+
## Workflow
96+
97+
1. Run Appwrite backend locally (see [docs](https://appwrite.io/docs/advanced/self-hosting))
98+
2. Configure `.env` with backend endpoint
99+
3. `pnpm install --frozen-lockfile`
100+
4. `pnpm dev` (hot reload on port 3000)
101+
5. Before commit: `pnpm run check && pnpm run format && pnpm run lint && pnpm run test && pnpm run build`
102+
6. **Take screenshots**: For any UI changes, capture screenshots and include them in the PR description or comments before finalizing
103+
104+
**Trust these instructions** - only search if incomplete/incorrect. See CONTRIBUTING.md for PR conventions. Use `--frozen-lockfile` always. Docker builds: multi-stage, final image is nginx serving static files from `/console` path.

Dockerfile

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ ADD ./vite.config.ts /app/vite.config.ts
2020
ADD ./src /app/src
2121
ADD ./static /app/static
2222

23+
ARG PUBLIC_IMAGINE_CDN_URL
2324
ARG PUBLIC_CONSOLE_MODE
2425
ARG PUBLIC_CONSOLE_PROFILE
2526
ARG PUBLIC_AI_SERVICE_BASE_URL
@@ -45,6 +46,7 @@ ENV PUBLIC_CONSOLE_MOCK_AI_SUGGESTIONS=$PUBLIC_CONSOLE_MOCK_AI_SUGGESTIONS
4546
ENV PUBLIC_STRIPE_KEY=$PUBLIC_STRIPE_KEY
4647
ENV SENTRY_AUTH_TOKEN=$SENTRY_AUTH_TOKEN
4748
ENV SENTRY_RELEASE=$SENTRY_RELEASE
49+
ENV PUBLIC_IMAGINE_CDN_URL=$PUBLIC_IMAGINE_CDN_URL
4850
ENV NODE_OPTIONS=--max_old_space_size=8192
4951

5052
RUN pnpm run build
@@ -55,3 +57,8 @@ EXPOSE 80
5557

5658
COPY docker/nginx.conf /etc/nginx/conf.d/default.conf
5759
COPY --from=build /app/build /usr/share/nginx/html/console
60+
61+
# feat-profiles
62+
RUN mkdir -p /app
63+
COPY docker/generate-env.sh /app/generate-env.sh
64+
RUN chmod +x /app/generate-env.sh

docker/generate-env.sh

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
#!/bin/sh
2+
set -e
3+
4+
# Regenerate env.js from runtime PUBLIC_ environment variables
5+
6+
echo "========================================="
7+
echo "Regenerating env.js from environment variables..."
8+
echo "========================================="
9+
10+
# Check if any PUBLIC_ env vars exist
11+
public_vars=$(printenv | grep '^PUBLIC_' | awk -F= '{print $1}' | sort)
12+
13+
if [ -z "$public_vars" ]; then
14+
echo "⚠ WARNING: No PUBLIC_ environment variables found!"
15+
echo "The application may not work correctly."
16+
echo "Expected variables like:"
17+
echo " - PUBLIC_APPWRITE_ENDPOINT"
18+
echo " - PUBLIC_CONSOLE_MODE"
19+
echo " - PUBLIC_CONSOLE_PROFILE"
20+
echo "========================================="
21+
fi
22+
23+
# Build JSON object from PUBLIC_ env vars
24+
env_json="{"
25+
first=true
26+
27+
for var in $public_vars; do
28+
value=$(printenv "$var")
29+
30+
# Escape special characters for JSON
31+
escaped_value=$(echo "$value" | sed 's/\\/\\\\/g' | sed 's/"/\\"/g')
32+
33+
if [ "$first" = true ]; then
34+
first=false
35+
else
36+
env_json="$env_json,"
37+
fi
38+
39+
env_json="$env_json\"$var\":\"$escaped_value\""
40+
echo "$var=$value"
41+
done
42+
43+
env_json="$env_json}"
44+
45+
# Write to env.js
46+
# Use CONSOLE_BUILD_PATH env var if set, otherwise try common locations
47+
if [ -n "$CONSOLE_BUILD_PATH" ]; then
48+
env_file="$CONSOLE_BUILD_PATH/_app/env.js"
49+
elif [ -f "/usr/share/nginx/html/console/_app/env.js" ]; then
50+
env_file="/usr/share/nginx/html/console/_app/env.js"
51+
elif [ -f "./build/_app/env.js" ]; then
52+
env_file="./build/_app/env.js"
53+
elif [ -f "./_app/env.js" ]; then
54+
env_file="./_app/env.js"
55+
else
56+
echo "⚠ Error: Cannot find env.js file. Set CONSOLE_BUILD_PATH env var to the build directory."
57+
echo "Searched in:"
58+
echo " - /usr/share/nginx/html/console/_app/env.js"
59+
echo " - ./build/_app/env.js"
60+
echo " - ./_app/env.js"
61+
exit 1
62+
fi
63+
64+
echo "Writing to: $env_file"
65+
echo "export const env=$env_json" > "$env_file"
66+
67+
# Verify the file was written
68+
if [ ! -f "$env_file" ]; then
69+
echo "⚠ Error: Failed to write $env_file"
70+
exit 1
71+
fi
72+
73+
echo "✓ Successfully generated $env_file"
74+
echo "File contents:"
75+
cat "$env_file"
76+
77+
# NUKE all pre-compressed files so nginx serves fresh content
78+
echo "Nuking all .br and .gz files..."
79+
find /usr/share/nginx/html/console -type f \( -name "*.br" -o -name "*.gz" \) -delete
80+
echo "✓ Nuked all compressed files"
81+
82+
echo "========================================="
83+
echo "Starting nginx..."
84+
echo "========================================="
85+
86+
exec "$@"

e2e/steps/free-project.ts

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,22 @@ export async function createFreeProject(page: Page): Promise<Metadata> {
1717
await page.waitForURL(/\/organization-[^/]+/);
1818
await page.getByRole('button', { name: 'create project' }).first().click();
1919
const dialog = page.locator('dialog[open]');
20+
2021
await dialog.getByPlaceholder('Project name').fill('test project');
22+
23+
let region = 'fra'; // for fallback
24+
const regionPicker = dialog.locator('button[role="combobox"]');
25+
if (await regionPicker.isVisible()) {
26+
await regionPicker.click();
27+
await page.getByRole('option', { name: /New York/i }).click();
28+
29+
region = 'nyc';
30+
}
31+
2132
await dialog.getByRole('button', { name: 'create' }).click();
22-
await page.waitForURL(/\/project-fra-[^/]+/);
23-
expect(page.url()).toContain('/console/project-fra-');
33+
34+
await page.waitForURL(new RegExp(`/project-${region}-[^/]+`));
35+
expect(page.url()).toContain(`/console/project-${region}-`);
2436

2537
return getProjectIdFromUrl(page.url());
2638
});

e2e/steps/pro-project.ts

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,10 +50,21 @@ export async function createProProject(page: Page): Promise<Metadata> {
5050
await page.waitForURL(/\/organization-[^/]+/);
5151
await page.getByRole('button', { name: 'create project' }).first().click();
5252
const dialog = page.locator('dialog[open]');
53+
5354
await dialog.getByPlaceholder('Project name').fill('test project');
55+
56+
let region = 'fra'; // for fallback
57+
const regionPicker = dialog.locator('button[role="combobox"]');
58+
if (await regionPicker.isVisible()) {
59+
await regionPicker.click();
60+
await page.getByRole('option', { name: /New York/i }).click();
61+
62+
region = 'nyc';
63+
}
64+
5465
await dialog.getByRole('button', { name: 'create' }).click();
55-
await page.waitForURL(/\/project-fra-[^/]+/);
56-
expect(page.url()).toContain('/console/project-fra-');
66+
await page.waitForURL(new RegExp(`/project-${region}-[^/]+`));
67+
expect(page.url()).toContain(`/console/project-${region}-`);
5768

5869
return getProjectIdFromUrl(page.url());
5970
});

0 commit comments

Comments
 (0)