Skip to content

Commit f6d564d

Browse files
ruggiRheeseyb
andauthored
Express server (#4987)
* add motion * fetcher with operation * operation * use the new fetchers * split storage * active operation divs * cleanup operations * types * op with key * use v3_fetcherPersist * no need to check null * separate operation component * wip * correct state flow * remove debug * remove wait * adjust cleanup * add spacing * split hooks * more efficient hooks * comment * more comment * easier to read * simplify a bit * a much simpler hook * split * document the code and split it * 6 digits * fix color * remove unused * move down * fix project passing * adjust assertion * getOperationDescription * unless * wip * add env vars * clean up deps * i'll still call it "cool proxy" * remove unneeded dep * use originalrequest's proto * express template + integrate with remix and hot reload * pfft * comment and section * ascii banner * remove line ref * fallback for auth0 * add routes * update ports and proxied routes * update route * fix(all) locally run express on 8000 and 8001, and haskell on 8002 * comment out * remove * fix equality check * fix links * rename * fix finish flow * finish mirroring routes * add monitoring route * adjust default headers * revert pointless change * clean up * Update utopia-remix/readme.md Co-authored-by: RheeseyB <1044774+Rheeseyb@users.noreply.github.com> * Update utopia-remix/readme.md Co-authored-by: RheeseyB <1044774+Rheeseyb@users.noreply.github.com> * Update utopia-remix/readme.md Co-authored-by: RheeseyB <1044774+Rheeseyb@users.noreply.github.com> * update validators * use let * pin dev deps --------- Co-authored-by: RheeseyB <1044774+Rheeseyb@users.noreply.github.com>
1 parent e99c8e1 commit f6d564d

19 files changed

+438
-46
lines changed

server/src/Utopia/Web/Executors/Common.hs

+2-2
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ type Stop = IO ()
9595
data EnvironmentRuntime r = EnvironmentRuntime
9696
{ _initialiseResources :: IO r
9797
, _startup :: r -> IO Stop
98-
, _envServerPort :: r -> [Int]
98+
, _envServerPort :: r -> Int
9999
, _serverAPI :: r -> Server API
100100
, _startupLogging :: r -> Bool
101101
, _getLogger :: r -> FastLogger
@@ -139,7 +139,7 @@ logoutOfSession sessionState cookie pageContents action = do
139139
portFromEnvironment :: IO Int
140140
portFromEnvironment = do
141141
fromEnvironment <- lookupEnv "PORT"
142-
let portForEndpoint = fromMaybe 8000 $ do
142+
let portForEndpoint = fromMaybe 8002 $ do
143143
envPort <- fromEnvironment
144144
readMaybe envPort
145145
return portForEndpoint

server/src/Utopia/Web/Executors/Development.hs

+2-2
Original file line numberDiff line numberDiff line change
@@ -521,8 +521,8 @@ startup DevServerResources{..} = do
521521
killThread hashedFilenamesThread
522522
destroyAllResources _projectPool
523523

524-
serverPortFromResources :: DevServerResources -> [Int]
525-
serverPortFromResources resources = [_serverPort resources, _serverPort resources + 1]
524+
serverPortFromResources :: DevServerResources -> Int
525+
serverPortFromResources resources = _serverPort resources
526526

527527
shouldProxyWebpack :: IO Bool
528528
shouldProxyWebpack = do

server/src/Utopia/Web/Executors/Production.hs

+2-2
Original file line numberDiff line numberDiff line change
@@ -467,8 +467,8 @@ startup ProductionServerResources{..} = do
467467
killThread hashedFilenamesThread
468468
destroyAllResources _projectPool
469469

470-
serverPortFromResources :: ProductionServerResources -> [Int]
471-
serverPortFromResources resources = [_serverPort resources]
470+
serverPortFromResources :: ProductionServerResources -> Int
471+
serverPortFromResources resources = _serverPort resources
472472

473473
productionEnvironmentRuntime :: EnvironmentRuntime ProductionServerResources
474474
productionEnvironmentRuntime = EnvironmentRuntime

server/src/Utopia/Web/Server.hs

+4-5
Original file line numberDiff line numberDiff line change
@@ -176,14 +176,13 @@ runServerWithResources EnvironmentRuntime{..} = do
176176
when loggingEnabled $ loggerLn logger "Starting"
177177
shutdown <- _startup resources
178178
when loggingEnabled $ loggerLn logger "Startup Processes Completed"
179-
let ports = _envServerPort resources
179+
let port = _envServerPort resources
180180
when loggingEnabled $ do
181-
forM_ ports $ \port -> do
182-
-- Note: '<>' is used to append text (amongst other things).
183-
loggerLn logger ("Running On: http://localhost:" <> toLogStr port <> "/")
181+
-- Note: '<>' is used to append text (amongst other things).
182+
loggerLn logger ("Running On: http://localhost:" <> toLogStr port <> "/")
184183
let storeForMetrics = _metricsStore resources
185184
meterMap <- mkMeterMap apiProxy storeForMetrics
186-
let settingsList = [ Warp.setPort port $ Warp.setOnException (exceptionHandler (loggerLn logger)) Warp.defaultSettings | port <- ports ]
185+
let settingsList = [Warp.setPort port $ Warp.setOnException (exceptionHandler (loggerLn logger)) Warp.defaultSettings]
187186
let serverAPI = _serverAPI resources
188187
let assetsCache = _cacheForAssets resources
189188
let shouldForceSSL = _forceSSL resources

shell.nix

+1-1
Original file line numberDiff line numberDiff line change
@@ -554,7 +554,7 @@ let
554554
cd $(${pkgs.git}/bin/git rev-parse --show-toplevel)/utopia-remix
555555
${pnpm}/bin/pnpm install
556556
${pnpm}/bin/pnpm exec prisma generate
557-
PORT=8002 ${pnpm}/bin/pnpm run dev
557+
PORT=8000 ${pnpm}/bin/pnpm run dev
558558
'')
559559
];
560560

utopia-remix/.env.sample

+8-5
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
APP_ENV="local"
2-
BACKEND_URL="http://127.0.0.1:8001"
2+
BACKEND_URL="http://127.0.0.1:8002"
33
CORS_ORIGIN="http://localhost:8000"
44
DATABASE_URL="postgres://<username>:postgres@localhost:5432/utopia"
55
REACT_APP_EDITOR_URL="http://localhost:8000"
@@ -8,14 +8,17 @@ AUTH0_ENDPOINT=""
88
AUTH0_CLIENT_ID=""
99
AUTH0_REDIRECT_URI=""
1010

11+
GITHUB_OAUTH_CLIENT_ID=""
12+
GITHUB_OAUTH_REDIRECT_URL=""
13+
14+
UTOPIA_CDN_URL="http://localhost:8000"
15+
1116
FGA_STORE_ID=""
1217
FGA_CLIENT_ID=""
1318
FGA_SECRET=""
1419
FGA_API_HOST="api.us1.fga.dev"
1520
FGA_API_TOKEN_ISSUER="fga.us.auth0.com"
1621
FGA_API_AUDIENCE="https://api.us1.fga.dev/"
1722

18-
GITHUB_OAUTH_CLIENT_ID=""
19-
GITHUB_OAUTH_REDIRECT_URL=""
20-
21-
UTOPIA_CDN_URL="http://localhost:8000"
23+
EXPRESS_PROXY_TARGET_HOST="localhost"
24+
EXPRESS_PROXY_TARGET_PORT="8002"
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import { LoaderFunctionArgs } from '@remix-run/node'
2+
import { proxy } from '../util/proxy.server'
3+
import { handle, handleOptions } from '../util/api.server'
4+
import { ALLOW } from '../handlers/validators'
5+
6+
// This is for monitoring/debugging the HS server, exposing it for added convenience.
7+
export async function loader(args: LoaderFunctionArgs) {
8+
return handle(args, {
9+
OPTIONS: handleOptions,
10+
GET: { handler: proxy, validator: ALLOW },
11+
})
12+
}

utopia-remix/app/routes/v1.asset.$id.assets.$name.tsx

+17-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
import { ActionFunctionArgs, LoaderFunctionArgs } from '@remix-run/node'
22
import { proxy } from '../util/proxy.server'
33
import { handle, handleOptions } from '../util/api.server'
4-
import { ALLOW } from '../handlers/validators'
4+
import { ALLOW, validateProjectAccess } from '../handlers/validators'
5+
import { UserProjectPermission } from '../types'
56

67
export async function loader(args: LoaderFunctionArgs) {
78
return handle(args, {
@@ -13,7 +14,21 @@ export async function action(args: ActionFunctionArgs) {
1314
return handle(args, {
1415
POST: {
1516
handler: (req) => proxy(req, { rawOutput: true }),
16-
validator: ALLOW,
17+
validator: validateProjectAccess(UserProjectPermission.CAN_MANAGE_PROJECT, {
18+
getProjectId: (params) => params.id,
19+
}),
20+
},
21+
DELETE: {
22+
handler: proxy,
23+
validator: validateProjectAccess(UserProjectPermission.CAN_MANAGE_PROJECT, {
24+
getProjectId: (params) => params.id,
25+
}),
26+
},
27+
PUT: {
28+
handler: proxy,
29+
validator: validateProjectAccess(UserProjectPermission.CAN_MANAGE_PROJECT, {
30+
getProjectId: (params) => params.id,
31+
}),
1732
},
1833
})
1934
}
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,24 @@
11
import { LoaderFunctionArgs } from '@remix-run/node'
2-
import { handle, handleOptions } from '../util/api.server'
2+
import { ensure, handle, handleOptions } from '../util/api.server'
33
import { proxy } from '../util/proxy.server'
44
import { ALLOW } from '../handlers/validators'
5+
import { Status } from '../util/statusCodes'
56

67
export async function loader(args: LoaderFunctionArgs) {
78
return handle(args, {
89
OPTIONS: handleOptions,
9-
GET: { handler: proxy, validator: ALLOW },
10+
GET: { handler: handleFinish, validator: ALLOW },
11+
})
12+
}
13+
14+
async function handleFinish(req: Request) {
15+
const proxyResponse = await proxy(req, { rawOutput: true })
16+
ensure(proxyResponse instanceof Response, 'invalid response', Status.INTERNAL_ERROR)
17+
ensure(proxyResponse.ok, proxyResponse.statusText, proxyResponse.status)
18+
19+
const body = await proxyResponse.text()
20+
return new Response(body, {
21+
headers: { 'content-type': 'text/html' },
22+
status: 200,
1023
})
1124
}

utopia-remix/app/routes/v1.user.config.tsx

+7-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { LoaderFunctionArgs } from '@remix-run/node'
1+
import { ActionFunctionArgs, LoaderFunctionArgs } from '@remix-run/node'
22
import { proxy } from '../util/proxy.server'
33
import { handle, handleOptions } from '../util/api.server'
44
import { ALLOW } from '../handlers/validators'
@@ -9,3 +9,9 @@ export async function loader(args: LoaderFunctionArgs) {
99
GET: { handler: proxy, validator: ALLOW },
1010
})
1111
}
12+
13+
export async function action(args: ActionFunctionArgs) {
14+
return handle(args, {
15+
POST: { handler: proxy, validator: ALLOW },
16+
})
17+
}

utopia-remix/app/test-util.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,7 @@ export function newTestRequest(params?: {
109109
formData?: FormData
110110
}): Request {
111111
const path = (params?.path ?? '').replace(/^\/+/, '')
112-
const req = new Request(`http://localhost:8002/` + path, {
112+
const req = new Request(`http://localhost:8000/` + path, {
113113
method: params?.method,
114114
body: params?.formData,
115115
})

utopia-remix/app/util/api.server.ts

+19-15
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,12 @@ import * as cookie from 'cookie'
44
import { UserDetails } from 'prisma-client'
55
import { PrismaClientKnownRequestError } from 'prisma-client/runtime/library.js'
66
import invariant from 'tiny-invariant'
7-
import { ServerEnvironment } from '../env.server'
7+
import { ALLOW } from '../handlers/validators'
88
import { getUserFromSession } from '../models/session.server'
99
import { ApiError } from './errors'
1010
import { Method } from './methods.server'
1111
import { Status } from './statusCodes'
12-
import { ALLOW } from '../handlers/validators'
12+
import { ServerEnvironment } from '../env.server'
1313

1414
interface ErrorResponse {
1515
error: string
@@ -19,13 +19,15 @@ type EmptyResponse = Record<string, never>
1919

2020
export type ApiResponse<T> = TypedResponse<T | ErrorResponse | EmptyResponse>
2121

22-
const defaultResponseHeaders = new Headers({
23-
'Access-Control-Allow-Origin': ServerEnvironment.CORSOrigin,
24-
'Access-Control-Allow-Credentials': 'true',
25-
'Access-Control-Allow-Headers': 'content-type, origin, cookie',
26-
'Access-Control-Allow-Methods': 'GET,POST,PUT,DELETE,OPTIONS',
27-
'Cache-control': 'no-cache',
28-
})
22+
function defaultResponseHeaders(): Headers {
23+
return new Headers({
24+
'Access-Control-Allow-Origin': ServerEnvironment.CORSOrigin,
25+
'Access-Control-Allow-Credentials': 'true',
26+
'Access-Control-Allow-Headers': 'content-type, origin, cookie',
27+
'Access-Control-Allow-Methods': 'GET,POST,PUT,DELETE,OPTIONS',
28+
'Cache-control': 'no-cache',
29+
})
30+
}
2931

3032
export function alwaysAllow(
3133
handler: (request: Request, params: Params<string>) => Promise<unknown>,
@@ -37,7 +39,7 @@ export function alwaysAllow(
3739
}
3840

3941
async function optionsHandler(): Promise<TypedResponse<EmptyResponse>> {
40-
return json({}, { headers: defaultResponseHeaders })
42+
return json({}, { headers: defaultResponseHeaders() })
4143
}
4244
export const handleOptions = alwaysAllow(optionsHandler)
4345

@@ -79,26 +81,27 @@ async function handleMethod<T>(
7981
if (validator != null) {
8082
await validator(request, params)
8183
}
84+
8285
const resp = await fn(request, params)
8386
if (resp instanceof Response) {
84-
const mergedHeaders = new Headers(defaultResponseHeaders)
87+
let headers = new Headers()
8588
resp.headers.forEach((value, key) => {
86-
mergedHeaders.set(key, value)
89+
headers.set(key, value)
8790
})
8891
return new Response(resp.body, {
8992
status: resp.status,
90-
headers: mergedHeaders,
93+
headers: headers,
9194
})
9295
}
93-
return json(resp, { headers: defaultResponseHeaders })
96+
return json(resp, { headers: { ...defaultResponseHeaders() } })
9497
} catch (err) {
9598
const { message, status, name } = getErrorData(err)
9699

97100
console.error(`${request.method} ${request.url}: ${message}`)
98101

99102
return json(
100103
{ error: name, status: status, message: message },
101-
{ headers: defaultResponseHeaders, status: status },
104+
{ headers: defaultResponseHeaders(), status: status },
102105
)
103106
}
104107
}
@@ -145,6 +148,7 @@ export async function proxiedResponse(response: Response): Promise<unknown> {
145148
}
146149

147150
export const SESSION_COOKIE_NAME = 'JSESSIONID'
151+
148152
function getSessionId(request: Request): string | null {
149153
const cookieHeader = request.headers.get('cookie') ?? ''
150154
const cookies = cookie.parse(cookieHeader)

utopia-remix/app/util/auth0.server.ts

+18-1
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,30 @@
1+
import urlJoin from 'url-join'
12
import { ServerEnvironment } from '../env.server'
23

34
export function auth0LoginURL(): string {
5+
const behaviour = 'auto-close'
6+
7+
const useAuth0 =
8+
ServerEnvironment.AUTH0_ENDPOINT !== '' &&
9+
ServerEnvironment.AUTH0_CLIENT_ID !== '' &&
10+
ServerEnvironment.AUTH0_REDIRECT_URI !== ''
11+
if (!useAuth0) {
12+
console.warn(
13+
'Auth0 is disabled, if you need it be sure to set the AUTH0_ENDPOINT, AUTH0_CLIENT_ID, AUTH0_REDIRECT_URI environment variables',
14+
)
15+
const url = new URL(urlJoin(ServerEnvironment.BackendURL, 'authenticate'))
16+
url.searchParams.set('code', 'logmein')
17+
url.searchParams.set('onto', behaviour)
18+
return url.href
19+
}
20+
421
const url = new URL(`https://${ServerEnvironment.AUTH0_ENDPOINT}/authorize`)
522
url.searchParams.set('scope', 'openid profile email')
623
url.searchParams.set('response_type', 'code')
724
url.searchParams.set('client_id', ServerEnvironment.AUTH0_CLIENT_ID)
825

926
const redirectURL = new URL(ServerEnvironment.AUTH0_REDIRECT_URI)
10-
redirectURL.searchParams.set('onto', 'auto-close')
27+
redirectURL.searchParams.set('onto', behaviour)
1128

1229
url.searchParams.set('redirect_uri', redirectURL.href)
1330
return url.href

utopia-remix/package.json

+10-3
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,9 @@
55
"type": "module",
66
"scripts": {
77
"build": "remix build",
8-
"dev": "remix dev --manual",
8+
"dev": "remix dev -c \"node server.js\"",
99
"lint": "eslint --ignore-path .gitignore --cache --cache-location ./node_modules/.cache/eslint .",
10-
"start": "remix-serve ./build/index.js",
10+
"start": "node ./server.js",
1111
"typecheck": "tsc",
1212
"format": "prettier --write .",
1313
"test": "./run-integration-tests.sh",
@@ -24,14 +24,17 @@
2424
"@radix-ui/react-tooltip": "1.0.7",
2525
"@radix-ui/themes": "2.0.3",
2626
"@remix-run/css-bundle": "2.5.1",
27+
"@remix-run/express": "2.8.0",
2728
"@remix-run/node": "2.5.1",
2829
"@remix-run/react": "2.5.1",
2930
"@remix-run/serve": "2.5.1",
3031
"classnames": "2.5.1",
3132
"@tailwindcss/aspect-ratio": "0.4.2",
3233
"@tailwindcss/typography": "0.5.10",
3334
"cookie": "0.6.0",
35+
"cors": "2.8.5",
3436
"dotenv": "16.4.1",
37+
"express": "4.18.3",
3538
"framer-motion": "11.0.6",
3639
"isbot": "4",
3740
"moment": "2.30.1",
@@ -49,6 +52,8 @@
4952
"@babel/preset-env": "7.23.9",
5053
"@remix-run/dev": "2.5.1",
5154
"@types/cookie": "0.6.0",
55+
"@types/cors": "2.8.17",
56+
"@types/express": "4.17.21",
5257
"@types/jest": "29.5.12",
5358
"@types/node": "20.11.15",
5459
"@types/react": "18.2.20",
@@ -59,6 +64,7 @@
5964
"@vanilla-extract/dynamic": "2.1.0",
6065
"@vanilla-extract/recipes": "0.5.1",
6166
"@vanilla-extract/sprinkles": "1.6.1",
67+
"chokidar": "3.6.0",
6268
"eslint": "8.38.0",
6369
"eslint-import-resolver-typescript": "3.6.1",
6470
"eslint-plugin-import": "2.28.1",
@@ -68,7 +74,8 @@
6874
"jest": "29.7.0",
6975
"prettier": "3.0.3",
7076
"prisma": "5.9.0",
71-
"tailwindcss": "^3.4.1",
77+
"source-map-support": "0.5.21",
78+
"tailwindcss": "3.4.1",
7279
"ts-node": "10.9.2",
7380
"typescript": "5.1.6"
7481
},

0 commit comments

Comments
 (0)