Skip to content

Commit 4342741

Browse files
authored
Merge pull request #65 from JDIZM/JDI-15-support-workspaces
JDI 15 support workspaces
2 parents d0d5557 + c78a98a commit 4342741

Some content is hidden

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

53 files changed

+1624
-870
lines changed

.env.example

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,17 @@
1-
NODE_ENV=development
1+
NODE_ENV=dev
22
APP_URL=http://localhost:4000
33
PORT=4000
44

55
###
66
## Database
77
###
8-
# replace with host.docker.internal if using docker for database and the app is running on host
9-
# POSTGRES_HOST=host.docker.internal
10-
POSTGRES_HOST=localhost
8+
POSTGRES_HOST=db
119
POSTGRES_USER=postgres
1210
POSTGRES_PASSWORD=example
13-
POSTGRES_DB=test
11+
POSTGRES_DB=postgres
1412

1513
###
1614
## Supabase config
1715
###
18-
SUPABASE_URL=https://xxxxx.supabase.co
19-
SUPABASE_PK=
16+
SUPABASE_URL=https://example.supabase.co
17+
SUPABASE_PK=example-key

.eslintrc.cjs

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,25 +4,32 @@ module.exports = {
44
es2022: true,
55
node: true
66
},
7+
parser: "@typescript-eslint/parser",
78
extends: [
89
// By extending from a plugin config, we can get recommended rules without having to add them manually.
910
"eslint:recommended",
1011
"plugin:import/recommended",
1112
"plugin:@typescript-eslint/recommended",
13+
"plugin:import/errors",
14+
"plugin:import/warnings",
15+
"plugin:import/typescript",
1216
// This disables the formatting rules in ESLint that Prettier is going to be responsible for handling.
1317
// Make sure it's always the last config, so it gets the chance to override other configs.
1418
"eslint-config-prettier",
1519
"prettier"
1620
],
21+
plugins: ["@typescript-eslint", "import"],
1722
settings: {
1823
// Tells eslint how to resolve imports
1924
"import/resolver": {
2025
// using the newer eslint-import-resolver-typescript plugin
2126
// see: https://www.npmjs.com/package/eslint-import-resolver-typescript
22-
node: true,
27+
node: {
28+
extensions: [".js", ".jsx", ".ts", ".tsx", ".d.ts"]
29+
},
2330
typescript: {
24-
alwaysTryTypes: true
25-
// project: "./tsconfig.json" // relative to project root.
31+
alwaysTryTypes: true,
32+
directory: "./tsconfig.json"
2633
}
2734
},
2835
"import/parsers": {
@@ -32,7 +39,7 @@ module.exports = {
3239
rules: {
3340
// Add your own rules here to override ones from the extended configs.
3441
"@typescript-eslint/no-explicit-any": "warn",
35-
"arrow-parens": "error"
42+
"arrow-parens": ["error", "always"]
3643
},
3744
overrides: [
3845
{

.gitignore

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,6 @@ node_modules
22
dist
33
coverage
44
.env
5-
package-lock.json
5+
pnpm-lock.yaml
6+
logs
7+
*.log

Dockerfile

Lines changed: 40 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,50 @@
1-
# use a slimmer alpine image to consume less memory
2-
# https://viralganatra.com/docker-nodejs-production-secure-best-practices/
3-
FROM node:20-alpine
1+
FROM node:20-alpine as base
2+
3+
ENV PNPM_VERSION=9.1.0
4+
5+
RUN npm install -g pnpm@$PNPM_VERSION
46

57
WORKDIR /app
68

79
# install deps first so we can cache them
8-
COPY package*.json ./
9-
RUN npm ci && npm cache clean --force
10+
COPY package*.json pnpm-lock.yaml ./
11+
RUN pnpm install --frozen-lockfile
12+
13+
FROM base AS builder
14+
15+
WORKDIR /app
1016

11-
# build the app
1217
COPY . .
18+
1319
RUN mkdir dist
14-
RUN npm run build
20+
21+
RUN pnpm build
22+
23+
FROM base AS runner
24+
25+
WORKDIR /app
26+
27+
RUN addgroup --system --gid 1001 express
28+
RUN adduser --system --uid 1001 express
29+
30+
COPY --from=builder --chown=express:express /app/node_modules /app/node_modules
31+
COPY --from=builder --chown=express:express /app/dist /app/dist
32+
COPY --from=builder --chown=express:express /app/package.json /app/package.json
33+
34+
USER express
35+
36+
EXPOSE 4000
37+
38+
FROM runner AS dev
39+
40+
WORKDIR /app
41+
42+
COPY --from=builder --chown=express:express /app/.env /app/.env
1543

1644
CMD ["node", "./dist/server.mjs"]
1745

46+
FROM runner AS prod
47+
48+
WORKDIR /app
49+
50+
CMD ["node", "./dist/server.mjs"]

README.md

Lines changed: 69 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,20 @@
1717
- [helmet](https://helmetjs.github.io/)
1818
- [cookie-parser](https://www.npmjs.com/package/cookie-parser)
1919

20-
A simple node/express backend api template.
20+
A node/express backend API template for getting started with a new project that includes authentication, permissions, and a database configured to
21+
use [Supabase](https://supabase.io/) or a local/cloud Postgres database.
22+
23+
This comes pre-defined with a workspaces model that allows accounts (users) to create workspaces and invite other profiles (users presence within a workspace) to access the workspace (membership). see the [Permissions](#Permissions) section for more information on how permissions are defined.
24+
25+
The contents of a workspace is not defined in this template and can be customized to suit the needs of the project.
26+
27+
## ESM Node
28+
29+
https://www.typescriptlang.org/docs/handbook/esm-node.html
30+
31+
This project has been setup to use ESM Node. This allows us to use ES6 imports in Node.
32+
33+
This uses [tsx](https://github.com/esbuild-kit/tsx) as a dev server and [pkgroll](https://github.com/privatenumber/pkgroll) to bundle and build the project.
2134

2235
## Requirements
2336

@@ -29,15 +42,11 @@ To install volta run the following command in the terminal.
2942
curl https://get.volta.sh | bash
3043
```
3144

32-
## ESM Node
33-
34-
https://www.typescriptlang.org/docs/handbook/esm-node.html
35-
36-
This project has been setup to use ESM Node. This allows us to use ES6 imports in Node.
45+
You will need a Postgres database to run this project. You can use Docker to run a Postgres database or use a service like [Supabase](https://supabase.com/).
3746

38-
This uses [tsx](https://github.com/esbuild-kit/tsx) as a dev server and [pkgroll](https://github.com/privatenumber/pkgroll) to bundle and build the project.
47+
See the [Database](#Database) section for more information on how to configure the database connection.
3948

40-
## ENV
49+
### ENV
4150

4251
Create a .env file in the root of the project and copy the contents of .env.example into it.
4352

@@ -47,20 +56,11 @@ cp .env.example .env
4756

4857
see the section on [Deployment with DigitalOcean](#deployment-with-digitalocean) for more information on how to configure the environment variables for deployment in different environments (eg. development and production).
4958

50-
## Setup
59+
### Install dependencies
5160

5261
```
5362
# install dependencies
5463
npm i
55-
56-
# start the dev server
57-
npm run dev
58-
59-
# make sure to configure the env variables
60-
cp .env.example .env
61-
62-
# view it running on localhost
63-
curl localhost:3000
6464
```
6565

6666
## Testing
@@ -75,45 +75,39 @@ It's also recommended to install the [vitest extension for vscode](https://marke
7575

7676
You can view the database with `npx drizzle-kit studio` or `npm run studio`.
7777

78-
You can spin up a local copy of the database with `docker-compose` but this is not required when using Supabase.
78+
You can spin up a local copy of the database and application with `docker-compose` but this is not required when using the Supabase db.
7979

8080
```
8181
docker compose up -d
8282
```
8383

84-
If you are using the local database and running the application within docker on the host machine you will need to replace the `POSTGRES_HOST` from `localhost` to `host.docker.internal` in the .env file.
85-
86-
```
87-
POSTGRES_HOST=host.docker.internal
88-
```
84+
Alternatively you can create a local network and connect the containers to the network.
8985

90-
## Build with docker
86+
```bash
87+
docker network create mynetwork
9188

89+
docker run --network mynetwork --name mypostgres -d -p 5432:5432 -e POSTGRES_USER=postgres -e POSTGRES_PASSWORD=example -e POSTGRES_DB=postgres postgres:15
9290
```
93-
# build the app
94-
npm run build
95-
96-
# build with docker
97-
docker build . --tag node-express-esm
9891

99-
# or to build with a specific platform
100-
docker build . --tag node-express-esm --platform linux/amd64
92+
Then when running the application in docker you can connect to the database with the container name.
10193

102-
# start the docker container
103-
docker run -d -p 3000:3000 node-express-esm
94+
```bash
95+
POSTGRES_HOST=mypostgres
96+
```
10497

105-
# view it running on localhost
106-
curl localhost:3000
98+
Then run the application in docker and connect to the same network.
99+
```bash
100+
docker run --network mynetwork --name node-express -d -p 4000:4000 node-express
107101
```
108102

103+
Note: If you are using a local database and running the application within docker on the host machine you will need to set `POSTGRES_HOST=host.docker.internal` in the .env file. [Read the docs for more info](https://docs.docker.com/desktop/networking/#i-want-to-connect-from-a-container-to-a-service-on-the-host)
104+
109105
### Migrations
110106

111107
When the schema/model is changed make sure to create a new migration and run it against the db.
112108

113109
1. Create a new migration
114110

115-
<!-- TODO create a named migration and pass additional flag to npm. -->
116-
117111
```
118112
npm run migrate:create
119113
@@ -139,6 +133,28 @@ npm run seed
139133

140134
Be sure to update the seeds as new migrations are added.
141135

136+
## Build with docker
137+
138+
```
139+
# build the app
140+
npm run build
141+
142+
# build with docker
143+
docker build . --tag node-express
144+
145+
# or to build with a specific platform
146+
docker build . --tag node-express --platform linux/amd64
147+
148+
# or build a specific stage eg dev
149+
docker build . --target dev --tag node-express
150+
151+
# start the docker container
152+
docker run -d -p 4000:4000 node-express
153+
154+
# view it running on localhost
155+
curl localhost:4000
156+
```
157+
142158
## Import aliases
143159

144160
Aliases can be configured in the import map, defined in package.json#imports.
@@ -153,24 +169,28 @@ This project uses JWT bearer token for authentication. The claims, id and sub mu
153169

154170
How permissions work.
155171

156-
A resource will have a permission level. A user will have a role/claim.
172+
A resource will have a permission level for each route method based on users role within the workspace. Workspace permissions can be defined in `./src/helpers/permissions.ts`.
173+
174+
Workspace permissions:
175+
Admin: Highest level of access to all resources within the workspace.
176+
User: Regular user with limited permissions.
157177

158-
Routes will have their permission level defined in `./src/helpers/permissions.ts`
178+
Resource permissions:
179+
Owner: Has access to their own resources
159180

160-
When a user makes a request to a route the route will check the user's role/claim against the permission level of the resource.
181+
Account permissions:
182+
SuperAdmin: Has access to all super only resources.
161183

162-
### Route permission levels
184+
### Workspace route permission levels
163185

164-
1. Owner - Route can only be accessed by the owner of the resource. Defined by the id of the resource being accessed matching the id of the user making the request.
165-
2. User - Can access all resources with user permissions.
166-
3. Admin - Can access all resources.
186+
Ensure every request that requires workspace permissions includes a workspace context. This can be done using the `x-workspace-id header`.
167187

168-
### Claims / Roles
188+
Passing the `x-workspace-id` header will allow the user to access the workspace resources if they are a member of the workspace with a sufficient role.
169189

170-
A claim is defined when the user is created which defines the user's role and permissions level.
190+
A role/claim is defined when the account is added to the workspace as a member.
171191

172-
1. User - default user permissions
173-
2. Admin - admin permissions
192+
1. User - Can access all resources with user permissions.
193+
2. Admin - Can access all resources.
174194

175195
## Supabase Auth
176196

docker-compose.yml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,14 @@
22
version: "3.1"
33

44
services:
5+
app:
6+
build:
7+
context: .
8+
dockerfile: Dockerfile
9+
env_file:
10+
- .env
11+
ports:
12+
- "4000:4000"
513
db:
614
image: postgres
715
restart: always

drizzle.config.ts

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,20 @@
11
import type { Config } from "drizzle-kit";
22
// drizzle requires the ts extension to import config.
33
import { config } from "./src/config.ts";
4-
import { logger } from "./src/helpers/logger.ts";
4+
import { logger } from "./src/helpers/index.ts";
55

6-
logger.info("config", config);
6+
logger.info({ msg: "config", config });
77

88
export default {
9+
dialect: "postgresql",
910
schema: "./src/schema.ts",
1011
out: "./drizzle",
11-
driver: "pg",
1212
dbCredentials: {
1313
host: config.db_host,
1414
user: config.db_user,
1515
port: 5432,
1616
password: config.db_password,
17-
database: config.db_name
17+
database: config.db_name,
18+
ssl: config.env !== "dev"
1819
}
1920
} satisfies Config;

drizzle/0000_puzzling_warpath.sql

Lines changed: 0 additions & 8 deletions
This file was deleted.

0 commit comments

Comments
 (0)