Skip to content

Commit

Permalink
chore(examples): Update Linearlite to use the Proxy and new types (#590)
Browse files Browse the repository at this point in the history
This updates linearlite to use the Proxy and new types.

Of the new times we are using `timestamp` and 'uuid'.

The only difference between the files from the starter and their copies
in this example is that they have been renamed from `*.js` to `*.cjs`.
This example has `"type": "module"` set in `package.json`, by renaming
the scripts to `*.cjs` they continue to work unmodified. Without this
they would have to be converted to ESM with "import" rather than
"require" syntax.

`builder.cjs` is included in the project but not used to build the app,
it uses Vite. It is inspected for port definitions by other scripts from
the starter and so cannot be deleted without modifying them.
  • Loading branch information
samwillis authored Nov 1, 2023
1 parent 3fdb289 commit bfce42e
Show file tree
Hide file tree
Showing 29 changed files with 4,295 additions and 1,971 deletions.
4 changes: 3 additions & 1 deletion examples/linearlite/backend/compose/.envrc
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
# Docker images for the local stack
export POSTGRESQL_IMAGE=postgres:14-alpine
export APP_NAME=linearlite
export ELECTRIC_PORT=5133
export ELECTRIC_PROXY_PORT=65432
export ELECTRIC_IMAGE=electricsql/electric:latest
export APP_NAME=linearlite
21 changes: 12 additions & 9 deletions examples/linearlite/backend/compose/docker-compose.yaml
Original file line number Diff line number Diff line change
@@ -1,42 +1,45 @@
version: '3.8'
version: "3.8"
name: "${APP_NAME:-electric}"

configs:
postgres_config:
file: './postgres/postgres.conf'
file: "./postgres/postgres.conf"

volumes:
pg_data:

services:
postgres:
image: '${POSTGRESQL_IMAGE:-postgres:14-alpine}'
image: "${POSTGRESQL_IMAGE:-postgres:14-alpine}"
environment:
POSTGRES_DB: ${APP_NAME:-electric}
POSTGRES_USER: postgres
POSTGRES_PASSWORD: password
POSTGRES_PASSWORD: pg_password
command:
- -c
- config_file=/etc/postgresql.conf
configs:
- source: postgres_config
target: /etc/postgresql.conf
healthcheck:
test: ['CMD-SHELL', 'pg_isready -U postgres']
test: ["CMD-SHELL", "pg_isready -U postgres"]
extra_hosts:
- 'host.docker.internal:host-gateway'
- "host.docker.internal:host-gateway"
ports:
- 5432
volumes:
- pg_data:/var/lib/postgresql/data

electric:
image: '${ELECTRIC_IMAGE:-electricsql/electric:latest}'
image: "${ELECTRIC_IMAGE:-electricsql/electric:latest}"
init: true
environment:
DATABASE_URL: postgresql://postgres:password@postgres:5432/${APP_NAME:-electric}
DATABASE_URL: postgresql://postgres:pg_password@postgres:5432/${APP_NAME:-electric}
PG_PROXY_PASSWORD: proxy_password
LOGICAL_PUBLISHER_HOST: electric
AUTH_MODE: insecure
ports:
- 5133:5133
- ${ELECTRIC_PORT:-5133}:5133
- ${ELECTRIC_PROXY_PORT:-65432}:65432
depends_on:
- postgres
16 changes: 16 additions & 0 deletions examples/linearlite/backend/startCompose.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
const { dockerCompose } = require('../util/util.cjs')
const process = require('process')

const cliArguments = process.argv.slice(2)

dockerCompose('up', cliArguments, (code) => {
if (code !== 0) {
console.error(
'\x1b[31m',
'Failed to start the Electric backend. Check the output from `docker compose` above.\n' +
'If the error message mentions a port already being allocated or address being already in use,\n' +
'execute `yarn ports:configure` to run Electric on another port.',
'\x1b[0m'
)
}
})
81 changes: 81 additions & 0 deletions examples/linearlite/backend/startElectric.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
const shell = require('shelljs')

let db = process.env.DATABASE_URL
let electricPort = process.env.ELECTRIC_PORT ?? 5133
let electricProxyPort = process.env.ELECTRIC_PROXY_PORT ?? 65432

let args = process.argv.slice(2)

while (args.length > 0) {
// There are arguments to parse
const flag = args[0]
const value = args[1]

args = args.slice(2)

const checkValue = () => {
if (typeof value === 'undefined') {
error(`Missing value for option '${flag}'.`)
}
}

switch (flag) {
case '-db':
checkValue()
db = value
break
case '--electric-port':
checkValue()
electricPort = parsePort(value)
break
case '--electric-proxy-port':
checkValue()
electricProxyPort = parsePort(value)
break
default:
error(`Unrecognized option: '${flag}'.`)
}
}

function parsePort(port) {
// checks that the number is between 0 and 65535
const portRegex = /^([1-9][0-9]{0,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])$/
if (!portRegex.test(port)) {
error(`Invalid port '${port}. Port should be between 0 and 65535.'`)
}
return port
}

if (db === undefined) {
console.error(`Database URL is not provided. Please provide one using the DATABASE_URL environment variable.`)
process.exit(1)
}

const electric = process.env.ELECTRIC_IMAGE ?? "electricsql/electric:latest"

// 5433 is the logical publisher port
// it is exposed because PG must be able to connect to Electric
const res = shell.exec(
`docker run \
-e "DATABASE_URL=${db}" \
-e "LOGICAL_PUBLISHER_HOST=localhost" \
-e "AUTH_MODE=insecure" \
-p ${electricPort}:5133 \
-p ${electricProxyPort}:65432 \
-p 5433:5433 ${electric}`
)

if (res.code !== 0 && res.stderr.includes('port is already allocated')) {
// inform the user that they should change ports
console.error(
'\x1b[31m',
'Could not start Electric because the port seems to be taken.\n' +
'To run Electric on another port execute `yarn ports:configure`',
'\x1b[0m'
)
}

function error(err) {
console.error('\x1b[31m', err + '\nyarn electric:start [-db <Postgres connection url>] [--electric-port <port>] [--electric-proxy-port <port>]', '\x1b[0m')
process.exit(1)
}
40 changes: 0 additions & 40 deletions examples/linearlite/backend/startElectric.js

This file was deleted.

99 changes: 99 additions & 0 deletions examples/linearlite/builder.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
const { build, serve } = require('esbuild')

const { createServer, request } = require('http')
const { spawn } = require('child_process')

const fs = require('fs-extra')
const inlineImage = require('esbuild-plugin-inline-image')

const shouldMinify = process.env.NODE_ENV === 'production'
const shouldServe = process.env.SERVE === 'true'

// https://github.com/evanw/esbuild/issues/802#issuecomment-819578182
const liveServer = (buildOpts) => {
const clients = []

build(
{
...buildOpts,
banner: { js: ' (() => new EventSource("/esbuild").onmessage = () => location.reload())();' },
watch: {
onRebuild(error, result) {
clients.forEach((res) => res.write('data: update\n\n'))
clients.length = 0
console.log(error ? error : '...')
},
}
}
).catch(() => process.exit(1))

serve({servedir: 'dist' }, {})
.then((serveResult) => {
createServer((req, res) => {
const { url, method, headers } = req

if (url === '/esbuild')
return clients.push(
res.writeHead(200, {
'Content-Type': 'text/event-stream',
'Cache-Control': 'no-cache',
Connection: 'keep-alive',
})
)

const path = ~url.split('/').pop().indexOf('.') ? url : `/index.html` //for PWA with router
req.pipe(
request({ hostname: '0.0.0.0', port: serveResult.port, path, method, headers }, (prxRes) => {
res.writeHead(prxRes.statusCode, prxRes.headers)
prxRes.pipe(res, { end: true })
}),
{ end: true }
)
}).listen(3001)

setTimeout(() => {
const op = { darwin: ['open'], linux: ['xdg-open'], win32: ['cmd', '/c', 'start'] }
const ptf = process.platform
const url = "http://localhost:3001"
if (clients.length === 0) spawn(op[ptf][0], [...[op[ptf].slice(1)], url])
console.info(`Your app is running at ${url}`)
}, 500) // open the default browser only if it is not opened yet
})
}

/**
* ESBuild Params
* @link https://esbuild.github.io/api/#build-api
*/
let buildParams = {
color: true,
entryPoints: ["src/index.tsx"],
loader: { ".ts": "tsx" },
outdir: "dist",
minify: shouldMinify,
format: "cjs",
bundle: true,
sourcemap: true,
logLevel: "error",
incremental: true,
define: {
__DEBUG_MODE__: JSON.stringify(process.env.DEBUG_MODE === 'true'),
__ELECTRIC_URL__: JSON.stringify(process.env.ELECTRIC_URL ?? 'ws://localhost:5133'),
},
external: ["fs", "path"],
plugins: [inlineImage()],
};

(async () => {
fs.removeSync("dist");
fs.copySync("public", "dist");

if (shouldServe) {
liveServer(buildParams)
}
else {
await build(buildParams)

process.exit(0)
}
})();
Loading

0 comments on commit bfce42e

Please sign in to comment.