|
1 | 1 | # constructive-functions |
2 | 2 |
|
3 | | -Functions playground for Constructive — isolated workspace for building, testing, and deploying Knative-style HTTP functions. |
| 3 | +Functions playground for Constructive — a workspace for building, testing, and deploying serverless HTTP functions backed by a Postgres-backed job queue. |
| 4 | + |
| 5 | +Functions are authored in `functions/<name>/` (a `handler.ts` plus a `handler.json` manifest), generated into runnable workspace packages by `pnpm generate`, and dispatched by the job service in `job/service/`. Templates live in `templates/` (`node-graphql` and `python` are supported today). |
4 | 6 |
|
5 | 7 | ## Quick Start |
6 | 8 |
|
7 | 9 | ```bash |
8 | | -# Install dependencies |
9 | | -pnpm install |
10 | | - |
11 | | -# Build all functions |
12 | | -pnpm build |
| 10 | +pnpm generate # generate workspace packages from handler.json manifests |
| 11 | +pnpm install # install dependencies (including generated packages) |
| 12 | +pnpm build # build all packages and functions |
13 | 13 |
|
14 | | -# Build Docker images locally |
15 | | -make docker-build |
16 | | - |
17 | | -# Build individual function images |
18 | | -make docker-build-simple-email |
| 14 | +make docker-build # build all function Docker images |
| 15 | +make docker-build-simple-email # build a single function image |
19 | 16 | make docker-build-send-email-link |
20 | 17 | ``` |
21 | 18 |
|
22 | 19 | ## Functions |
23 | 20 |
|
24 | | -### `@constructive-io/simple-email-fn` |
| 21 | +| Function | Port | Type | Image | |
| 22 | +|----------|------|------|-------| |
| 23 | +| `simple-email` | 8081 | node-graphql | `ghcr.io/constructive-io/constructive-functions/simple-email:latest` | |
| 24 | +| `send-email-link` | 8082 | node-graphql | `ghcr.io/constructive-io/constructive-functions/send-email-link:latest` | |
| 25 | +| `knative-job-example` | 8083 | node-graphql | `ghcr.io/constructive-io/constructive-functions/knative-job-example:latest` | |
| 26 | +| `python-example` | 8084 | python | `ghcr.io/constructive-io/constructive-functions/python-example:latest` | |
25 | 27 |
|
26 | | -Simple email function that sends emails directly from job payload. |
| 28 | +Port `8080` is reserved for the job service. |
27 | 29 |
|
28 | | -- **Port**: `8080` |
29 | | -- **Docker image**: `ghcr.io/constructive-io/constructive-functions/simple-email:latest` |
30 | | -- **Environment**: |
31 | | - - `SIMPLE_EMAIL_DRY_RUN` — If `true`, logs email payload without sending |
32 | | - - `MAILGUN_API_KEY`, `MAILGUN_KEY` — Mailgun credentials |
33 | | - - `MAILGUN_DOMAIN`, `MAILGUN_FROM`, `MAILGUN_REPLY` — Mailgun email config |
| 30 | +### `simple-email` |
34 | 31 |
|
35 | | -### `@constructive-io/send-email-link-fn` |
| 32 | +Sends emails directly from a job payload. |
36 | 33 |
|
37 | | -Email link function for invite, password reset, and verification emails. |
| 34 | +- `SIMPLE_EMAIL_DRY_RUN` — if `true`, logs the payload instead of sending |
| 35 | +- `MAILGUN_API_KEY`, `MAILGUN_KEY`, `MAILGUN_DOMAIN`, `MAILGUN_FROM`, `MAILGUN_REPLY` — Mailgun config |
38 | 36 |
|
39 | | -- **Port**: `8080` |
40 | | -- **Docker image**: `ghcr.io/constructive-io/constructive-functions/send-email-link:latest` |
41 | | -- **Environment**: |
42 | | - - `SEND_EMAIL_LINK_DRY_RUN` — If `true`, logs email payload without sending |
43 | | - - `DEFAULT_DATABASE_ID` — Default database UUID |
44 | | - - `GRAPHQL_URL` — GraphQL API endpoint |
45 | | - - `META_GRAPHQL_URL` — Meta GraphQL API endpoint (optional) |
46 | | - - `GRAPHQL_AUTH_TOKEN` — Optional Bearer token for GraphQL requests |
47 | | - - `LOCAL_APP_PORT` — Local port for dashboard links (e.g., `3000`) |
48 | | - - `MAILGUN_*` — Same Mailgun config as `simple-email` |
| 37 | +### `send-email-link` |
49 | 38 |
|
50 | | -## Development |
| 39 | +Sends invite, password reset, and verification emails (rendered via MJML). |
51 | 40 |
|
52 | | -This repo includes full Kubernetes manifests for deploying functions to any cluster. |
| 41 | +- `SEND_EMAIL_LINK_DRY_RUN` — if `true`, logs the payload instead of sending |
| 42 | +- `DEFAULT_DATABASE_ID` — default database UUID |
| 43 | +- `GRAPHQL_URL`, `META_GRAPHQL_URL` — GraphQL API endpoints |
| 44 | +- `GRAPHQL_AUTH_TOKEN` — optional Bearer token for GraphQL requests |
| 45 | +- `LOCAL_APP_PORT` — local port for dashboard links (e.g. `3000`) |
| 46 | +- `MAILGUN_*` — same Mailgun config as `simple-email` |
53 | 47 |
|
54 | | -### Local Kubernetes (kind/minikube) |
| 48 | +### `knative-job-example` / `python-example` |
55 | 49 |
|
56 | | -```bash |
57 | | -cd k8s |
| 50 | +Reference implementations for the `node-graphql` and `python` templates. |
58 | 51 |
|
59 | | -# Install Knative Serving + Kourier only (no CNPG) |
60 | | -make operators-knative-only |
| 52 | +## Development |
61 | 53 |
|
62 | | -# Apply local overlay (single Postgres, single MinIO, functions) |
63 | | -make kustomize-local |
| 54 | +The full local-dev guide lives in [DEVELOPMENT.md](./DEVELOPMENT.md). Two paths: |
64 | 55 |
|
65 | | -# Port-forward to local services |
66 | | -make proxy-server # GraphQL API -> localhost:8080 |
67 | | -make proxy-explorer # GraphQL explorer UI -> localhost:8081 |
68 | | -make proxy-web # Dashboard UI -> localhost:3000 |
69 | | -``` |
| 56 | +- **Docker Compose + local Node** (fastest iteration) — `make dev` for infrastructure, `make dev-fn` to run functions as local Node processes. |
| 57 | +- **Skaffold on local k8s** (production-like, with hot reload) — `make skaffold-dev` deploys the full stack to the `constructive-functions` namespace and watches handler files. |
70 | 58 |
|
71 | | -See `k8s/DEVELOPMENT_LOCAL.md` for detailed local setup. |
| 59 | +For the Knative variant of the k8s setup, see `k8s/DEVELOPMENT_LOCAL.md`. |
72 | 60 |
|
73 | 61 | ### CI/CD |
74 | 62 |
|
75 | | -The `CI Test K8s` workflow (`.github/workflows/test-k8s-deployment.yaml`) runs on every PR/push to `main` that touches `k8s/**`. It: |
76 | | - |
77 | | -1. Spins up a `kind` cluster |
78 | | -2. Installs Knative via `make operators-knative-only` |
79 | | -3. Applies the `k8s/overlays/ci` overlay |
80 | | -4. Waits for workloads to be ready |
| 63 | +The `CI Test K8s` workflow (`.github/workflows/test-k8s-deployment.yaml`) runs on PRs and pushes to `main` that touch `k8s/`, `tests/e2e/`, or `functions/`. It spins up a `kind` cluster, applies the `k8s/overlays/ci` overlay, and runs the per-function e2e tests. |
81 | 64 |
|
82 | 65 | ## Project Structure |
83 | 66 |
|
84 | 67 | ``` |
85 | 68 | . |
86 | | -├── functions/ |
87 | | -│ ├── send-email-link/ |
88 | | -│ │ ├── src/ |
89 | | -│ │ ├── package.json |
90 | | -│ │ ├── Dockerfile |
91 | | -│ │ └── tsconfig.json |
92 | | -│ └── simple-email/ |
93 | | -│ └── (same structure) |
| 69 | +├── functions/ # User-authored handler.ts + handler.json (git tracked) |
| 70 | +├── templates/ # Template definitions (node-graphql, python, shared, k8s) |
| 71 | +├── generated/ # Generated workspace packages (gitignored) |
| 72 | +├── packages/ |
| 73 | +│ ├── fn-runtime/ # createFunctionServer, GraphQL clients, FunctionContext |
| 74 | +│ └── fn-app/ # Express app factory with job callbacks |
| 75 | +├── job/ |
| 76 | +│ ├── service/ # Orchestrator (loads functions + worker + scheduler) |
| 77 | +│ ├── server/ # Callback receiver |
| 78 | +│ └── worker/ # Job dispatcher |
94 | 79 | ├── k8s/ |
95 | | -│ ├── base/ |
96 | | -│ ├── overlays/ |
97 | | -│ ├── scripts/ |
98 | | -│ └── Makefile |
99 | | -├── types/ |
100 | | -│ └── (custom type definitions for external packages) |
| 80 | +│ ├── base/ # Shared manifests |
| 81 | +│ └── overlays/ # local-simple, local, ci, dev, staging |
| 82 | +├── tests/ # integration + e2e suites |
| 83 | +├── scripts/ # generate.ts, dev.ts, docker-build.ts |
| 84 | +├── skaffold.yaml |
101 | 85 | ├── package.json |
102 | | -├── pnpm-workspace.yaml |
103 | 86 | └── Makefile |
104 | 87 | ``` |
105 | 88 |
|
106 | 89 | ## Notes |
107 | 90 |
|
108 | | -- Functions use `@constructive-io/knative-job-fn` for the Express/Knative HTTP wrapper |
109 | | -- Email providers use `@constructive-io/postmaster` (Mailgun) and `@launchql/mjml` (styled-email templates via MJML) |
110 | | -- No `docker-compose` — this repo is Kubernetes-focused for functions deployment |
111 | | -- Root workspace manages shared linting/formatting configs; each function has its own build config |
| 91 | +- Functions use `@constructive-io/fn-runtime` (runtime + GraphQL clients) and `@constructive-io/fn-app` (Express wrapper). |
| 92 | +- Email providers are wired through `@constructive-io/postmaster` (Mailgun) and `@launchql/mjml` / `@launchql/styled-email` for templating. |
| 93 | +- Both Node (`node-graphql`) and Python (`python`) templates are supported — pick via the `type` field in `handler.json`. |
| 94 | +- The `generated/` directory is entirely gitignored; rerun `pnpm generate` after changing any `handler.json`. |
| 95 | +- Root workspace manages shared linting/formatting; each generated function ships its own build config and Dockerfile. |
112 | 96 |
|
113 | 97 | ## Pushing Images |
114 | 98 |
|
115 | | -To push to GitHub Container Registry: |
| 99 | +Images are tagged with the GHCR prefix automatically: |
116 | 100 |
|
117 | 101 | ```bash |
118 | 102 | docker push ghcr.io/constructive-io/constructive-functions/simple-email:latest |
119 | 103 | docker push ghcr.io/constructive-io/constructive-functions/send-email-link:latest |
| 104 | +docker push ghcr.io/constructive-io/constructive-functions/knative-job-example:latest |
| 105 | +docker push ghcr.io/constructive-io/constructive-functions/python-example:latest |
120 | 106 | ``` |
121 | 107 |
|
122 | | -Images are built with the correct registry prefix via the Makefile: |
123 | | -- `make docker-build` — Builds all functions with `ghcr.io/constructive-io/constructive-functions/<name>:latest` |
124 | | -- `make docker-build-simple-email` — Builds `ghcr.io/constructive-io/constructive-functions/simple-email:latest` |
125 | | -- `make docker-build-send-email-link` — Builds `ghcr.io/constructive-io/constructive-functions/send-email-link:latest` |
| 108 | +- `make docker-build` — builds all function images |
| 109 | +- `make docker-build-<name>` — builds a single function image |
0 commit comments