mirror of
https://github.com/pockethost/pockethost.git
synced 2025-07-03 03:12:29 +00:00
v0.8 support
This commit is contained in:
parent
1661bf6821
commit
870870e62b
@ -1,4 +1,4 @@
|
||||
FROM node:18-alpine as buildbox
|
||||
FROM node:18-alpine as pockethost-buildbox
|
||||
COPY --from=golang:1.19-alpine /usr/local/go/ /usr/local/go/
|
||||
ENV PATH="/usr/local/go/bin:${PATH}"
|
||||
RUN apk add python3 py3-pip make gcc musl-dev g++ bash
|
||||
|
8
Dockerfile-prod
Normal file
8
Dockerfile-prod
Normal file
@ -0,0 +1,8 @@
|
||||
FROM node:18-alpine as pockethost-buildbox
|
||||
COPY --from=golang:1.19-alpine /usr/local/go/ /usr/local/go/
|
||||
ENV PATH="/usr/local/go/bin:${PATH}"
|
||||
RUN apk add python3 py3-pip make gcc musl-dev g++ bash
|
||||
WORKDIR /src
|
||||
COPY . .
|
||||
RUN yarn
|
||||
RUN yarn build
|
16
docker/build.yaml
Normal file
16
docker/build.yaml
Normal file
@ -0,0 +1,16 @@
|
||||
version: '3'
|
||||
|
||||
services:
|
||||
build:
|
||||
env_file:
|
||||
- .env.local
|
||||
build:
|
||||
context: ..
|
||||
dockerfile: Dockerfile
|
||||
container_name: build
|
||||
working_dir: /src
|
||||
command: bash -c "yarn && yarn build"
|
||||
volumes:
|
||||
- ./mount/cache/go:/go
|
||||
- ./mount/cache/yarn:/usr/local/share/.cache/yarn/v6
|
||||
- ..:/src
|
@ -1,39 +1,6 @@
|
||||
version: '3'
|
||||
|
||||
services:
|
||||
prepbox:
|
||||
env_file:
|
||||
- .env.local
|
||||
build:
|
||||
context: ..
|
||||
dockerfile: Dockerfile
|
||||
container_name: prepbox
|
||||
working_dir: /src
|
||||
command: bash -c "yarn"
|
||||
volumes:
|
||||
- ./mount/cache/go:/go
|
||||
- ./mount/cache/yarn:/usr/local/share/.cache/yarn/v6
|
||||
- ..:/src
|
||||
profiles: ['build']
|
||||
buildbox:
|
||||
environment:
|
||||
- GOPATH=/go
|
||||
env_file:
|
||||
- .env.local
|
||||
build:
|
||||
context: ..
|
||||
dockerfile: Dockerfile
|
||||
container_name: buildbox
|
||||
working_dir: /src
|
||||
command: bash -c "yarn build"
|
||||
volumes:
|
||||
- ./mount/cache/go:/go
|
||||
- ./mount/cache/yarn:/usr/local/share/.cache/yarn/v6
|
||||
- ..:/src
|
||||
depends_on:
|
||||
prepbox:
|
||||
condition: service_completed_successfully
|
||||
profiles: ['build']
|
||||
www:
|
||||
env_file:
|
||||
- .env.local
|
||||
@ -45,7 +12,6 @@ services:
|
||||
working_dir: /src
|
||||
command: bash -c "yarn dev:www --host=www"
|
||||
volumes:
|
||||
- ./mount/cache/yarn:/usr/local/share/.cache/yarn/v6
|
||||
- ..:/src
|
||||
networks:
|
||||
- app-network
|
||||
@ -54,7 +20,6 @@ services:
|
||||
depends_on:
|
||||
daemon:
|
||||
condition: service_started
|
||||
profiles: ['serve']
|
||||
daemon:
|
||||
env_file:
|
||||
- .env.local
|
||||
@ -67,14 +32,11 @@ services:
|
||||
restart: unless-stopped
|
||||
volumes:
|
||||
- ./mount/daemon/instances:/data
|
||||
- ./mount/cache/go:/go
|
||||
- ./mount/cache/yarn:/usr/local/share/.cache/yarn/v6
|
||||
- ..:/src
|
||||
networks:
|
||||
- app-network
|
||||
ports:
|
||||
- '9001:3000'
|
||||
profiles: ['serve']
|
||||
nginx:
|
||||
image: nginx:mainline-alpine
|
||||
container_name: nginx
|
||||
@ -91,7 +53,6 @@ services:
|
||||
- ./mount/nginx/ssl:/mount/nginx/ssl
|
||||
networks:
|
||||
- app-network
|
||||
profiles: ['serve']
|
||||
|
||||
networks:
|
||||
app-network:
|
16
docker/install.yaml
Normal file
16
docker/install.yaml
Normal file
@ -0,0 +1,16 @@
|
||||
version: '3'
|
||||
|
||||
services:
|
||||
prepbox:
|
||||
env_file:
|
||||
- .env.local
|
||||
build:
|
||||
context: ..
|
||||
dockerfile: Dockerfile
|
||||
container_name: prepbox
|
||||
working_dir: /src
|
||||
command: bash -c "yarn"
|
||||
volumes:
|
||||
- ./mount/cache/go:/go
|
||||
- ./mount/cache/yarn:/usr/local/share/.cache/yarn/v6
|
||||
- ..:/src
|
17
docker/migrate-dev.yaml
Normal file
17
docker/migrate-dev.yaml
Normal file
@ -0,0 +1,17 @@
|
||||
version: '3'
|
||||
|
||||
services:
|
||||
prepbox:
|
||||
env_file:
|
||||
- .env.local
|
||||
build:
|
||||
context: ..
|
||||
dockerfile: Dockerfile
|
||||
container_name: prepbox
|
||||
working_dir: /src
|
||||
command: bash -c "yarn migrate"
|
||||
volumes:
|
||||
- ./mount/daemon/instances:/data
|
||||
- ./mount/cache/go:/go
|
||||
- ./mount/cache/yarn:/usr/local/share/.cache/yarn/v6
|
||||
- ..:/src
|
17
docker/migrate-prod.yaml
Normal file
17
docker/migrate-prod.yaml
Normal file
@ -0,0 +1,17 @@
|
||||
version: '3'
|
||||
|
||||
services:
|
||||
prepbox:
|
||||
env_file:
|
||||
- .env.local
|
||||
build:
|
||||
context: ..
|
||||
dockerfile: Dockerfile
|
||||
container_name: prepbox
|
||||
working_dir: /src
|
||||
command: bash -c "yarn && yarn build && yarn migrate"
|
||||
volumes:
|
||||
- /home/pockethost/data:/data
|
||||
- ./mount/cache/go:/go
|
||||
- ./mount/cache/yarn:/usr/local/share/.cache/yarn/v6
|
||||
- ..:/src
|
@ -1,41 +1,6 @@
|
||||
version: '3'
|
||||
|
||||
services:
|
||||
prepbox:
|
||||
environment:
|
||||
- GOPATH=/go
|
||||
env_file:
|
||||
- .env.local
|
||||
build:
|
||||
context: ..
|
||||
dockerfile: Dockerfile
|
||||
container_name: prepbox
|
||||
working_dir: /src
|
||||
command: bash -c "yarn"
|
||||
volumes:
|
||||
- ./mount/cache/go:/go
|
||||
- ./mount/cache/yarn:/usr/local/share/.cache/yarn/v6
|
||||
- ..:/src
|
||||
profiles: ['build']
|
||||
buildbox:
|
||||
environment:
|
||||
- GOPATH=/go
|
||||
env_file:
|
||||
- .env.local
|
||||
build:
|
||||
context: ..
|
||||
dockerfile: Dockerfile
|
||||
container_name: buildbox
|
||||
working_dir: /src
|
||||
command: bash -c "yarn build"
|
||||
volumes:
|
||||
- ./mount/cache/go:/go
|
||||
- ./mount/cache/yarn:/usr/local/share/.cache/yarn/v6
|
||||
- ..:/src
|
||||
depends_on:
|
||||
prepbox:
|
||||
condition: service_completed_successfully
|
||||
profiles: ['build']
|
||||
www:
|
||||
env_file:
|
||||
- .env.local
|
||||
@ -47,7 +12,6 @@ services:
|
||||
working_dir: /src
|
||||
command: bash -c "yarn start:www --host=www"
|
||||
volumes:
|
||||
- ./mount/cache/yarn:/usr/local/share/.cache/yarn/v6
|
||||
- ..:/src
|
||||
networks:
|
||||
- app-network
|
||||
@ -56,7 +20,6 @@ services:
|
||||
depends_on:
|
||||
daemon:
|
||||
condition: service_started
|
||||
profiles: ['serve']
|
||||
daemon:
|
||||
env_file:
|
||||
- .env.local
|
||||
@ -71,14 +34,11 @@ services:
|
||||
- SHELL=/bin/bash
|
||||
volumes:
|
||||
- /home/pockethost/data:/data
|
||||
- ./mount/cache/go:/go
|
||||
- ./mount/cache/yarn:/usr/local/share/.cache/yarn/v6
|
||||
- ..:/src
|
||||
networks:
|
||||
- app-network
|
||||
ports:
|
||||
- '9001:3000'
|
||||
profiles: ['serve']
|
||||
nginx:
|
||||
image: nginx:mainline-alpine
|
||||
container_name: nginx
|
||||
@ -95,7 +55,6 @@ services:
|
||||
- ./mount/nginx/ssl:/mount/nginx/ssl
|
||||
networks:
|
||||
- app-network
|
||||
profiles: ['serve']
|
||||
|
||||
networks:
|
||||
app-network:
|
@ -15,7 +15,9 @@
|
||||
"dev:daemon": "cd packages/daemon && yarn dev",
|
||||
"start": "concurrently 'yarn start:www' 'yarn start:daemon'",
|
||||
"start:www": "cd packages/pockethost.io && yarn start",
|
||||
"start:daemon": "cd packages/daemon && yarn start"
|
||||
"start:daemon": "cd packages/daemon && yarn start",
|
||||
"migrate": "yarn migrate:daemon",
|
||||
"migrate:daemon": "cd packages/daemon && yarn migrate"
|
||||
},
|
||||
"workspaces": {
|
||||
"packages": [
|
||||
|
@ -31,7 +31,7 @@
|
||||
"cross-fetch": "^3.1.5",
|
||||
"eventsource": "^2.0.2",
|
||||
"find-up": "^6.3.0",
|
||||
"pocketbase": "^0.7.1",
|
||||
"pocketbase": "^0.8.0-rc1",
|
||||
"prompts": "^2.4.2",
|
||||
"tmp": "^0.2.1"
|
||||
},
|
||||
|
@ -6,6 +6,6 @@
|
||||
"dependencies": {
|
||||
"@s-libs/micro-dash": "^14.1.0",
|
||||
"nanoid": "^4.0.0",
|
||||
"pocketbase": "^0.7.0"
|
||||
"pocketbase": "^0.8.0-rc1"
|
||||
}
|
||||
}
|
||||
|
@ -1,25 +1,38 @@
|
||||
import PocketBase from 'pocketbase'
|
||||
import PocketBase, { Record } from 'pocketbase'
|
||||
import { CollectionName, RecordId } from './schema'
|
||||
|
||||
export interface RecordSubscription<T = Record> {
|
||||
action: string
|
||||
record: T
|
||||
}
|
||||
|
||||
export type RealtimeEventHandler<TRec> = (e: RecordSubscription<TRec>) => void
|
||||
|
||||
export const createRealtimeSubscriptionManager = (pocketbase: PocketBase) => {
|
||||
const subscriptions: { [_: string]: number } = {}
|
||||
|
||||
const subscribe = <TRec>(slug: string, cb: (rec: TRec) => void) => {
|
||||
const subscribeOne = <TRec>(
|
||||
collection: CollectionName,
|
||||
id: RecordId,
|
||||
cb: (e: RecordSubscription<TRec>) => void
|
||||
) => {
|
||||
const slug = `${collection}/${id}`
|
||||
if (subscriptions[slug]) {
|
||||
subscriptions[slug]++
|
||||
} else {
|
||||
subscriptions[slug] = 1
|
||||
pocketbase.realtime.subscribe(slug, (e) => {
|
||||
pocketbase.collection(collection).subscribeOne<TRec>(id, (e) => {
|
||||
console.log(`Realtime update`, { e })
|
||||
cb(e.record as unknown as TRec)
|
||||
cb(e)
|
||||
})
|
||||
}
|
||||
return () => {
|
||||
subscriptions[slug]--
|
||||
if (subscriptions[slug] === 0) {
|
||||
pocketbase.realtime.unsubscribe(slug)
|
||||
pocketbase.collection(collection).unsubscribe(id)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return subscribe
|
||||
return { subscribeOne }
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
export function assertExists<TType>(
|
||||
v: unknown,
|
||||
v: TType,
|
||||
message = `Value does not exist`
|
||||
): asserts v is NonNullable<TType> {
|
||||
if (typeof v === 'undefined') {
|
||||
|
@ -1,3 +1,4 @@
|
||||
export * from './assert'
|
||||
export * from './RealtimeSubscriptionManager'
|
||||
export * from './releases'
|
||||
export * from './schema'
|
||||
|
142
packages/common/src/release-names.txt
Normal file
142
packages/common/src/release-names.txt
Normal file
@ -0,0 +1,142 @@
|
||||
Abbey
|
||||
Ace
|
||||
Alice
|
||||
Alvin
|
||||
Amber
|
||||
Andy
|
||||
Bailey
|
||||
Bart
|
||||
Becca
|
||||
Belle
|
||||
Benny
|
||||
Biscuit
|
||||
Bonnie
|
||||
Boo
|
||||
Brooke
|
||||
Bruno
|
||||
Bunny
|
||||
Buttons
|
||||
Callie
|
||||
Carmen
|
||||
Cassidy
|
||||
Charlotte
|
||||
Chase
|
||||
Cheeks
|
||||
China
|
||||
Cindy
|
||||
Cinnamon
|
||||
Cody
|
||||
Cookie
|
||||
Crumpet
|
||||
Cupcake
|
||||
Daisy
|
||||
Dallas
|
||||
Dancer
|
||||
Dani
|
||||
Dasher
|
||||
Deckle
|
||||
Dirk
|
||||
Doily
|
||||
Dresden
|
||||
Droplet
|
||||
Elliott
|
||||
Emily
|
||||
Emma
|
||||
Fancy
|
||||
Fifi
|
||||
Fiona
|
||||
Flake
|
||||
Fluffy
|
||||
Frankie
|
||||
Frisky
|
||||
Frosty
|
||||
Frosty
|
||||
Giggles
|
||||
Ginger
|
||||
Goofball
|
||||
Gordon
|
||||
Gretel
|
||||
Gumdrop
|
||||
Gus
|
||||
Haley
|
||||
Hansel
|
||||
Harry
|
||||
Honeybunches
|
||||
Hugsgy
|
||||
Humphrey
|
||||
Hunny
|
||||
Ike
|
||||
Iris
|
||||
Jackie
|
||||
Jangles
|
||||
Jellybean
|
||||
Jenna
|
||||
Jesse
|
||||
Joey
|
||||
Josie
|
||||
Junior
|
||||
Kelly
|
||||
Kibbles
|
||||
Killer
|
||||
Kim
|
||||
Kit Kat
|
||||
Ladybug
|
||||
Laurie
|
||||
Leo
|
||||
Li’l Nibbler
|
||||
Maddie
|
||||
Maple Syrup
|
||||
Marshmallow
|
||||
Maximilian
|
||||
Maxine
|
||||
Mickey
|
||||
Mimi
|
||||
Mr. Gopher
|
||||
Muffin
|
||||
Nibbler
|
||||
Nibbly
|
||||
Nora
|
||||
Opal
|
||||
Oscar
|
||||
Pansy
|
||||
Peach
|
||||
Philbert
|
||||
Pierre
|
||||
Pixie
|
||||
Poofball
|
||||
Powderpuff
|
||||
Precious
|
||||
Puddin’
|
||||
Quinn
|
||||
Rainbow Dash
|
||||
Ralph
|
||||
Ripple
|
||||
Rockin’ Robin
|
||||
Rocky
|
||||
Rosie
|
||||
Rudolph
|
||||
Runt
|
||||
Rusty
|
||||
Sadie
|
||||
Sandy
|
||||
Sawyer
|
||||
Scout
|
||||
Simon
|
||||
Snowball
|
||||
Snuggles
|
||||
Sophie
|
||||
Spike
|
||||
Stella
|
||||
Sugarplum
|
||||
Sunny
|
||||
Sweetie Pie
|
||||
Sydney
|
||||
Tadpole
|
||||
Tammy
|
||||
Tater Tot
|
||||
Tori
|
||||
Twinkle
|
||||
Wally
|
||||
Whiskers
|
||||
Willow
|
||||
Zelda
|
57
packages/common/src/releases.ts
Normal file
57
packages/common/src/releases.ts
Normal file
@ -0,0 +1,57 @@
|
||||
import { find, last } from '@s-libs/micro-dash'
|
||||
import { assertExists } from './assert'
|
||||
|
||||
export const RELEASES = {
|
||||
ermine: {
|
||||
weight: 1,
|
||||
versions: ['0.7.9', '0.7.8', '0.7.7'],
|
||||
},
|
||||
lollipop: {
|
||||
weight: 2,
|
||||
versions: ['0.8.0-rc1'],
|
||||
},
|
||||
}
|
||||
|
||||
export type PlatformId = keyof typeof RELEASES
|
||||
export type VersionId = string
|
||||
|
||||
export const LATEST_PLATFORM: keyof typeof RELEASES = 'lollipop'
|
||||
|
||||
export const USE_LATEST_VERSION = 'latest'
|
||||
|
||||
function assertPlatform(platformId: string): asserts platformId is PlatformId {
|
||||
const hasPlatform = platformId in RELEASES
|
||||
if (!hasPlatform) {
|
||||
throw new Error(`Expected ${platformId} to exist here`)
|
||||
}
|
||||
}
|
||||
|
||||
export { assertPlatform }
|
||||
|
||||
export const versionFor = (platformId: PlatformId, version: VersionId) => {
|
||||
const platform = RELEASES[platformId]
|
||||
if (version === USE_LATEST_VERSION) {
|
||||
const _v = last(platform.versions)
|
||||
assertExists(_v, `Expected ${platformId} to have versions (latest)`)
|
||||
return _v
|
||||
}
|
||||
const _v = find(platform.versions, (v) => v === version)
|
||||
assertExists(_v, `Expected ${platformId} to have version (${version})`)
|
||||
return _v
|
||||
}
|
||||
|
||||
export const binFor = (
|
||||
platformId: PlatformId,
|
||||
version: VersionId = 'latest'
|
||||
) => {
|
||||
const _version = versionFor(platformId, version)
|
||||
return `pocketbase-${platformId}-${_version}`
|
||||
}
|
||||
|
||||
export const humanVersion = (platformId: PlatformId, version: VersionId) => {
|
||||
const platform = RELEASES[platformId]
|
||||
const _version = versionFor(platformId, version)
|
||||
const humanVersion =
|
||||
version === USE_LATEST_VERSION ? `${_version} (latest)` : _version
|
||||
return humanVersion
|
||||
}
|
@ -1,3 +1,5 @@
|
||||
import { PlatformId, VersionId } from './releases'
|
||||
|
||||
export type RecordId = string
|
||||
export type UserId = RecordId
|
||||
export type InstanceId = RecordId
|
||||
@ -8,6 +10,7 @@ export type IsoDate = string
|
||||
export type ProcessId = number
|
||||
export type Username = string
|
||||
export type Password = string
|
||||
export type CollectionName = string
|
||||
|
||||
export const pocketNow = () => new Date().toISOString()
|
||||
|
||||
@ -20,26 +23,21 @@ export enum InstanceStatus {
|
||||
Failed = 'failed',
|
||||
}
|
||||
|
||||
export type Instance_In = {
|
||||
uid?: UserId
|
||||
subdomain?: Subdomain
|
||||
status?: InstanceStatus
|
||||
}
|
||||
|
||||
export type PocketbaseRecord<TIdType extends RecordId> = {
|
||||
id: TIdType
|
||||
created: IsoDate
|
||||
updated: IsoDate
|
||||
}
|
||||
|
||||
export type Instance_Out = PocketbaseRecord<InstanceId> & {
|
||||
uid: UserId
|
||||
export type InstancesRecord = {
|
||||
id: RecordId
|
||||
subdomain: Subdomain
|
||||
uid: UserId
|
||||
status: InstanceStatus
|
||||
platform: PlatformId
|
||||
version: VersionId
|
||||
}
|
||||
|
||||
export type Any_Record_Out = Instance_Out
|
||||
export type InstancesRecord_New = Omit<InstancesRecord, 'id'>
|
||||
|
||||
export type Instance_Out_ByIdCollection = {
|
||||
[_: InstanceId]: Instance_Out
|
||||
export type UserRecord = {
|
||||
id: RecordId
|
||||
email: string
|
||||
verified: boolean
|
||||
}
|
||||
|
||||
export type InstanceRecordById = { [_: InstanceId]: InstancesRecord }
|
||||
|
@ -3,45 +3,24 @@
|
||||
"version": "0.0.1",
|
||||
"license": "MIT",
|
||||
"scripts": {
|
||||
"build": "mkdir -p dist && esbuild src/server.ts --bundle --platform=node > dist/server.js && echo 'Build complete' `date`",
|
||||
"dev": "yarn build && concurrently 'yarn dev:watch' 'yarn dev:serve'",
|
||||
"dev:watch": "chokidar 'src/**' '../pocketbase/src/**' -c 'yarn build'",
|
||||
"dev:serve": "nodemon dist/server.js",
|
||||
"start": "node dist/server.js"
|
||||
},
|
||||
"targets": {
|
||||
"server": {
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
},
|
||||
"source": "src/server.ts",
|
||||
"includeNodeModules": [
|
||||
"@pockethost/common",
|
||||
"get-port",
|
||||
"pocketbase",
|
||||
"@s-libs/micro-dash"
|
||||
]
|
||||
}
|
||||
"build": "echo 'Build complete' `date`",
|
||||
"dev": "tsx watch src/server.ts",
|
||||
"start": "tsx src/server.ts",
|
||||
"migrate": "tsx src/migrate/migrate.ts"
|
||||
},
|
||||
"dependencies": {
|
||||
"@pockethost/common": "0.0.1",
|
||||
"@s-libs/micro-dash": "^14.1.0",
|
||||
"@types/http-proxy": "^1.17.9",
|
||||
"@types/node": "^18.11.9",
|
||||
"bottleneck": "^2.19.5",
|
||||
"chokidar-cli": "^3.0.0",
|
||||
"event-source-polyfill": "^1.0.31",
|
||||
"get-port": "^6.1.2",
|
||||
"http-proxy": "^1.18.1",
|
||||
"node-fetch": "^3.2.10",
|
||||
"pocketbase": "^0.7.0",
|
||||
"ts-node": "^10.9.1"
|
||||
"pocketbase": "^0.8.0-rc1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"chokidar-cli": "^3.0.0",
|
||||
"concurrently": "^7.4.0",
|
||||
"esbuild": "^0.15.11",
|
||||
"nodemon": "^2.0.20",
|
||||
"parcel": "^2.7.0",
|
||||
"ts-node": "^10.9.1"
|
||||
"tsx": "^3.11.0"
|
||||
}
|
||||
}
|
@ -1,12 +1,9 @@
|
||||
import { InstanceStatus } from '@pockethost/common'
|
||||
import { binFor, InstanceStatus } from '@pockethost/common'
|
||||
import { forEachRight, map } from '@s-libs/micro-dash'
|
||||
import Bottleneck from 'bottleneck'
|
||||
import { ChildProcessWithoutNullStreams, spawn } from 'child_process'
|
||||
import { ChildProcessWithoutNullStreams } from 'child_process'
|
||||
import getPort from 'get-port'
|
||||
import fetch from 'node-fetch'
|
||||
import {
|
||||
DAEMON_PB_BIN_DIR,
|
||||
DAEMON_PB_DATA_DIR,
|
||||
DAEMON_PB_IDLE_TTL,
|
||||
DAEMON_PB_PASSWORD,
|
||||
DAEMON_PB_PORT_BASE,
|
||||
@ -17,8 +14,10 @@ import {
|
||||
PUBLIC_PB_PROTOCOL,
|
||||
PUBLIC_PB_SUBDOMAIN,
|
||||
} from './constants'
|
||||
import { collections_001 } from './migrations'
|
||||
import { createPbClient } from './PbClient'
|
||||
import { mkInternalUrl } from './util/internal'
|
||||
import { tryFetch } from './util/tryFetch'
|
||||
import { _spawn } from './util/_spawn'
|
||||
|
||||
type Instance = {
|
||||
process: ChildProcessWithoutNullStreams
|
||||
@ -29,80 +28,15 @@ type Instance = {
|
||||
startRequest: () => () => void
|
||||
}
|
||||
|
||||
const tryFetch = (url: string) =>
|
||||
new Promise<void>((resolve, reject) => {
|
||||
const tryFetch = () => {
|
||||
console.log(`Trying to connect to instance ${url} `)
|
||||
fetch(url)
|
||||
.then(() => {
|
||||
console.log(`Connection to ${url} successful`)
|
||||
resolve()
|
||||
})
|
||||
.catch((e) => {
|
||||
console.error(`Could not connect to ${url}`)
|
||||
setTimeout(tryFetch, 1000)
|
||||
})
|
||||
}
|
||||
tryFetch()
|
||||
})
|
||||
|
||||
const mkInternalAddress = (port: number) => `127.0.0.1:${port}`
|
||||
const mkInternalUrl = (port: number) => `http://${mkInternalAddress(port)}`
|
||||
|
||||
export const createInstanceManger = async () => {
|
||||
const instances: { [_: string]: Instance } = {}
|
||||
|
||||
const _spawn = async (cfg: {
|
||||
subdomain: string
|
||||
port: number
|
||||
bin: string
|
||||
}) => {
|
||||
const { subdomain, port, bin } = cfg
|
||||
const cmd = `${DAEMON_PB_BIN_DIR}/${bin}`
|
||||
|
||||
const args = [
|
||||
`serve`,
|
||||
`--dir`,
|
||||
`${DAEMON_PB_DATA_DIR}/${subdomain}/pb_data`,
|
||||
`--http`,
|
||||
mkInternalAddress(port),
|
||||
]
|
||||
console.log(`Spawning ${subdomain}`, { cmd, args })
|
||||
const ls = spawn(cmd, args)
|
||||
|
||||
ls.stdout.on('data', (data) => {
|
||||
console.log(`${subdomain} stdout: ${data}`)
|
||||
})
|
||||
|
||||
ls.stderr.on('data', (data) => {
|
||||
console.error(`${subdomain} stderr: ${data}`)
|
||||
})
|
||||
|
||||
ls.on('close', (code) => {
|
||||
console.log(`${subdomain} closed with code ${code}`)
|
||||
})
|
||||
ls.on('exit', (code) => {
|
||||
instances[subdomain]?.heartbeat(true)
|
||||
delete instances[subdomain]
|
||||
if (subdomain !== PUBLIC_PB_SUBDOMAIN) {
|
||||
client.updateInstanceStatus(subdomain, InstanceStatus.Idle)
|
||||
}
|
||||
console.log(`${subdomain} exited with code ${code}`)
|
||||
})
|
||||
ls.on('error', (err) => {
|
||||
console.log(`${subdomain} had error ${err}`)
|
||||
})
|
||||
|
||||
await tryFetch(mkInternalUrl(port))
|
||||
return ls
|
||||
}
|
||||
|
||||
const coreInternalUrl = mkInternalUrl(DAEMON_PB_PORT_BASE)
|
||||
const client = createPbClient(coreInternalUrl)
|
||||
const mainProcess = await _spawn({
|
||||
subdomain: PUBLIC_PB_SUBDOMAIN,
|
||||
port: DAEMON_PB_PORT_BASE,
|
||||
bin: 'pocketbase',
|
||||
bin: binFor('lollipop'),
|
||||
})
|
||||
instances[PUBLIC_PB_SUBDOMAIN] = {
|
||||
process: mainProcess,
|
||||
@ -118,7 +52,6 @@ export const createInstanceManger = async () => {
|
||||
await tryFetch(coreInternalUrl)
|
||||
try {
|
||||
await client.adminAuthViaEmail(DAEMON_PB_USERNAME, DAEMON_PB_PASSWORD)
|
||||
await client.migrate(collections_001)
|
||||
} catch (e) {
|
||||
console.error(
|
||||
`***WARNING*** CANNOT AUTHENTICATE TO ${PUBLIC_PB_PROTOCOL}://${PUBLIC_PB_SUBDOMAIN}.${PUBLIC_PB_DOMAIN}/_/`
|
||||
@ -160,7 +93,7 @@ export const createInstanceManger = async () => {
|
||||
console.log(`${subdomain} found in DB`)
|
||||
const exclude = map(instances, (i) => i.port)
|
||||
const newPort = await getPort({
|
||||
port: 8090,
|
||||
port: DAEMON_PB_PORT_BASE,
|
||||
exclude,
|
||||
}).catch((e) => {
|
||||
console.error(`Failed to get port for ${subdomain}`)
|
||||
@ -173,7 +106,15 @@ export const createInstanceManger = async () => {
|
||||
const childProcess = await _spawn({
|
||||
subdomain,
|
||||
port: newPort,
|
||||
bin: instance.bin || 'pocketbase',
|
||||
bin: binFor(instance.platform, instance.version),
|
||||
cleanup: (code) => {
|
||||
instances[subdomain]?.heartbeat(true)
|
||||
delete instances[subdomain]
|
||||
if (subdomain !== PUBLIC_PB_SUBDOMAIN) {
|
||||
client.updateInstanceStatus(subdomain, InstanceStatus.Idle)
|
||||
}
|
||||
console.log(`${subdomain} exited with code ${code}`)
|
||||
},
|
||||
})
|
||||
|
||||
const api: Instance = (() => {
|
||||
|
@ -1,19 +1,9 @@
|
||||
import { InstanceStatus } from '@pockethost/common'
|
||||
import PocketBase, { Record, User } from 'pocketbase'
|
||||
import { Collection_Serialized } from './migrations'
|
||||
|
||||
const safeCatch = <TIn extends any[], TOut>(
|
||||
name: string,
|
||||
cb: (...args: TIn) => Promise<TOut>
|
||||
) => {
|
||||
return (...args: TIn) => {
|
||||
console.log(`${name}`)
|
||||
return cb(...args).catch((e: any) => {
|
||||
console.error(`${name} failed: ${e}`)
|
||||
throw e
|
||||
})
|
||||
}
|
||||
}
|
||||
import { InstancesRecord, InstanceStatus, UserRecord } from '@pockethost/common'
|
||||
import { reduce } from '@s-libs/micro-dash'
|
||||
import Bottleneck from 'bottleneck'
|
||||
import PocketBase, { Collection } from 'pocketbase'
|
||||
import { Collection_Serialized } from './migrate/migrations'
|
||||
import { safeCatch } from './util/safeCatch'
|
||||
|
||||
export const createPbClient = (url: string) => {
|
||||
console.log(`Initializing client: ${url}`)
|
||||
@ -22,14 +12,15 @@ export const createPbClient = (url: string) => {
|
||||
const adminAuthViaEmail = safeCatch(
|
||||
`adminAuthViaEmail`,
|
||||
(email: string, password: string) =>
|
||||
client.admins.authViaEmail(email, password)
|
||||
client.admins.authWithPassword(email, password)
|
||||
)
|
||||
|
||||
const getInstanceBySubdomain = safeCatch(
|
||||
`getInstanceBySubdomain`,
|
||||
(subdomain: string): Promise<[Record, User] | []> =>
|
||||
client.records
|
||||
.getList(`instances`, 1, 1, {
|
||||
(subdomain: string): Promise<[InstancesRecord, UserRecord] | []> =>
|
||||
client
|
||||
.collection('instances')
|
||||
.getList<InstancesRecord>(1, 1, {
|
||||
filter: `subdomain = '${subdomain}'`,
|
||||
})
|
||||
.then((recs) => {
|
||||
@ -40,9 +31,12 @@ export const createPbClient = (url: string) => {
|
||||
}
|
||||
const [instance] = recs.items
|
||||
if (!instance) return []
|
||||
return client.users.getOne(instance.uid).then((user) => {
|
||||
return [instance, user]
|
||||
})
|
||||
return client
|
||||
.collection('users')
|
||||
.getOne<UserRecord>(instance.uid)
|
||||
.then((user) => {
|
||||
return [instance, user]
|
||||
})
|
||||
})
|
||||
)
|
||||
|
||||
@ -54,14 +48,41 @@ export const createPbClient = (url: string) => {
|
||||
if (!instance) {
|
||||
throw new Error(`Expected item here for ${subdomain}`)
|
||||
}
|
||||
await client.records.update(`instances`, instance.id, { status })
|
||||
await client.collection('instances').update(instance.id, { status })
|
||||
}
|
||||
)
|
||||
|
||||
const migrate = safeCatch(
|
||||
`migrate`,
|
||||
async (collections: Collection_Serialized[]) => {
|
||||
await client.collections.import(collections)
|
||||
await client.collections.import(collections as Collection[])
|
||||
}
|
||||
)
|
||||
|
||||
const updateInstances = safeCatch(
|
||||
'updateInstances',
|
||||
async (cb: (rec: InstancesRecord) => Partial<InstancesRecord>) => {
|
||||
const res = await client
|
||||
.collection('instances')
|
||||
.getFullList<InstancesRecord>(200)
|
||||
const limiter = new Bottleneck({ maxConcurrent: 1 })
|
||||
const promises = reduce(
|
||||
res,
|
||||
(c, r) => {
|
||||
c.push(
|
||||
limiter.schedule(() => {
|
||||
const toUpdate = cb(r)
|
||||
console.log(
|
||||
`Updating instnace ${r.id} with ${JSON.stringify(toUpdate)}`
|
||||
)
|
||||
return client.collection('instances').update(r.id, toUpdate)
|
||||
})
|
||||
)
|
||||
return c
|
||||
},
|
||||
[] as Promise<void>[]
|
||||
)
|
||||
await Promise.all(promises)
|
||||
}
|
||||
)
|
||||
|
||||
@ -70,5 +91,6 @@ export const createPbClient = (url: string) => {
|
||||
getInstanceBySubdomain,
|
||||
updateInstanceStatus,
|
||||
migrate,
|
||||
updateInstances,
|
||||
}
|
||||
}
|
||||
|
65
packages/daemon/src/migrate/migrate.ts
Normal file
65
packages/daemon/src/migrate/migrate.ts
Normal file
@ -0,0 +1,65 @@
|
||||
import { binFor, InstanceStatus } from '@pockethost/common'
|
||||
import { chdir } from 'process'
|
||||
import {
|
||||
DAEMON_PB_BIN_DIR,
|
||||
DAEMON_PB_DATA_DIR,
|
||||
DAEMON_PB_PASSWORD,
|
||||
DAEMON_PB_PORT_BASE,
|
||||
DAEMON_PB_USERNAME,
|
||||
PUBLIC_PB_DOMAIN,
|
||||
PUBLIC_PB_PROTOCOL,
|
||||
PUBLIC_PB_SUBDOMAIN,
|
||||
} from '../constants'
|
||||
import { createPbClient } from '../PbClient'
|
||||
import { mkInternalUrl } from '../util/internal'
|
||||
import { tryFetch } from '../util/tryFetch'
|
||||
import { _spawn } from '../util/_spawn'
|
||||
import { collections_001 } from './migrations'
|
||||
import { pexec } from './pexec'
|
||||
|
||||
const PB_BIN = `${DAEMON_PB_BIN_DIR}/${binFor('lollipop')}`
|
||||
const DATA_ROOT = `${DAEMON_PB_DATA_DIR}/${PUBLIC_PB_SUBDOMAIN}`
|
||||
|
||||
;(async () => {
|
||||
console.log(`Backing up`)
|
||||
chdir(DATA_ROOT)
|
||||
await pexec(`tar -czvf ${+new Date()}.tgz pb_data`)
|
||||
|
||||
console.log(`Upgrading`)
|
||||
await pexec(`${PB_BIN} upgrade --dir=pb_data`)
|
||||
|
||||
// Add `platform` and `bin` required columns (migrate db json)
|
||||
try {
|
||||
const mainProcess = await _spawn({
|
||||
subdomain: PUBLIC_PB_SUBDOMAIN,
|
||||
port: DAEMON_PB_PORT_BASE,
|
||||
bin: binFor('lollipop'),
|
||||
})
|
||||
try {
|
||||
const coreInternalUrl = mkInternalUrl(DAEMON_PB_PORT_BASE)
|
||||
const client = createPbClient(coreInternalUrl)
|
||||
await tryFetch(coreInternalUrl)
|
||||
await client.adminAuthViaEmail(DAEMON_PB_USERNAME, DAEMON_PB_PASSWORD)
|
||||
await client.migrate(collections_001)
|
||||
await client.updateInstances((instance) => {
|
||||
return {
|
||||
status: instance.status || InstanceStatus.Idle,
|
||||
platform: instance.platform || 'ermine',
|
||||
version: instance.version || 'latest',
|
||||
}
|
||||
})
|
||||
} catch (e) {
|
||||
console.error(
|
||||
`***WARNING*** CANNOT AUTHENTICATE TO ${PUBLIC_PB_PROTOCOL}://${PUBLIC_PB_SUBDOMAIN}.${PUBLIC_PB_DOMAIN}/_/`
|
||||
)
|
||||
console.error(
|
||||
`***WARNING*** LOG IN MANUALLY, ADJUST .env, AND RESTART DOCKER`
|
||||
)
|
||||
} finally {
|
||||
console.log(`Exiting process`)
|
||||
mainProcess.kill()
|
||||
}
|
||||
} catch (e) {
|
||||
console.error(`${e}`)
|
||||
}
|
||||
})()
|
@ -6,27 +6,90 @@ export type Collection_Serialized = Omit<Partial<Collection>, 'schema'> & {
|
||||
|
||||
export const collections_001: Collection_Serialized[] = [
|
||||
{
|
||||
id: 'systemprofiles0',
|
||||
name: 'profiles',
|
||||
system: true,
|
||||
listRule: 'userId = @request.user.id',
|
||||
viewRule: 'userId = @request.user.id',
|
||||
createRule: 'userId = @request.user.id',
|
||||
updateRule: 'userId = @request.user.id',
|
||||
deleteRule: null,
|
||||
id: 'etae8tuiaxl6xfv',
|
||||
name: 'instances',
|
||||
type: 'base',
|
||||
system: false,
|
||||
schema: [
|
||||
{
|
||||
id: 'pbfielduser',
|
||||
name: 'userId',
|
||||
type: 'user',
|
||||
system: true,
|
||||
id: 'qdtuuld1',
|
||||
name: 'subdomain',
|
||||
type: 'text',
|
||||
system: false,
|
||||
required: true,
|
||||
unique: true,
|
||||
options: {
|
||||
maxSelect: 1,
|
||||
cascadeDelete: true,
|
||||
min: null,
|
||||
max: 50,
|
||||
pattern: '^[a-z][\\-a-z]+$',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'rbj14krn',
|
||||
name: 'uid',
|
||||
type: 'relation',
|
||||
system: false,
|
||||
required: true,
|
||||
unique: false,
|
||||
options: {
|
||||
maxSelect: 1,
|
||||
collectionId: 'systemprofiles0',
|
||||
cascadeDelete: false,
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'c2y74d7h',
|
||||
name: 'status',
|
||||
type: 'text',
|
||||
system: false,
|
||||
required: true,
|
||||
unique: false,
|
||||
options: {
|
||||
min: null,
|
||||
max: null,
|
||||
pattern: '',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'yxby5r6b',
|
||||
name: 'platform',
|
||||
type: 'text',
|
||||
system: false,
|
||||
required: true,
|
||||
unique: false,
|
||||
options: {
|
||||
min: null,
|
||||
max: null,
|
||||
pattern: '',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: '4ydffkv3',
|
||||
name: 'version',
|
||||
type: 'text',
|
||||
system: false,
|
||||
required: true,
|
||||
unique: false,
|
||||
options: {
|
||||
min: null,
|
||||
max: null,
|
||||
pattern: '',
|
||||
},
|
||||
},
|
||||
],
|
||||
listRule: 'uid=@request.auth.id',
|
||||
viewRule: 'uid = @request.auth.id',
|
||||
createRule: "uid = @request.auth.id && (status = 'idle' || status = '')",
|
||||
updateRule: null,
|
||||
deleteRule: null,
|
||||
options: {},
|
||||
},
|
||||
{
|
||||
id: 'systemprofiles0',
|
||||
name: 'users',
|
||||
type: 'auth',
|
||||
system: false,
|
||||
schema: [
|
||||
{
|
||||
id: 'pbfieldname',
|
||||
name: 'name',
|
||||
@ -61,68 +124,20 @@ export const collections_001: Collection_Serialized[] = [
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 'etae8tuiaxl6xfv',
|
||||
name: 'instances',
|
||||
system: false,
|
||||
listRule: 'uid=@request.user.id',
|
||||
viewRule: 'uid = @request.user.id',
|
||||
createRule: "uid = @request.user.id && (status = 'idle' || status = '')",
|
||||
updateRule: null,
|
||||
listRule: 'id = @request.auth.id',
|
||||
viewRule: 'id = @request.auth.id',
|
||||
createRule: '',
|
||||
updateRule: 'id = @request.auth.id',
|
||||
deleteRule: null,
|
||||
schema: [
|
||||
{
|
||||
id: 'qdtuuld1',
|
||||
name: 'subdomain',
|
||||
type: 'text',
|
||||
system: false,
|
||||
required: true,
|
||||
unique: true,
|
||||
options: {
|
||||
min: null,
|
||||
max: 50,
|
||||
pattern: '^[a-z][\\-a-z]+$',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'rbj14krn',
|
||||
name: 'uid',
|
||||
type: 'user',
|
||||
system: false,
|
||||
required: true,
|
||||
unique: false,
|
||||
options: {
|
||||
maxSelect: 1,
|
||||
cascadeDelete: false,
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'c2y74d7h',
|
||||
name: 'status',
|
||||
type: 'text',
|
||||
system: false,
|
||||
required: false,
|
||||
unique: false,
|
||||
options: {
|
||||
min: null,
|
||||
max: null,
|
||||
pattern: '',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: '3rinhcnt',
|
||||
name: 'bin',
|
||||
type: 'text',
|
||||
system: false,
|
||||
required: false,
|
||||
unique: false,
|
||||
options: {
|
||||
min: 0,
|
||||
max: 30,
|
||||
pattern: '',
|
||||
},
|
||||
},
|
||||
],
|
||||
options: {
|
||||
allowEmailAuth: true,
|
||||
allowOAuth2Auth: true,
|
||||
allowUsernameAuth: false,
|
||||
exceptEmailDomains: null,
|
||||
manageRule: null,
|
||||
minPasswordLength: 8,
|
||||
onlyEmailDomains: null,
|
||||
requireEmail: true,
|
||||
},
|
||||
},
|
||||
]
|
16
packages/daemon/src/migrate/pexec.ts
Normal file
16
packages/daemon/src/migrate/pexec.ts
Normal file
@ -0,0 +1,16 @@
|
||||
import { exec } from 'child_process'
|
||||
|
||||
export const pexec = (cmd: string) => {
|
||||
return new Promise<void>((resolve, reject) => {
|
||||
console.log(cmd)
|
||||
exec(cmd, (err, stdout, stderr) => {
|
||||
console.log(stdout)
|
||||
console.error(stderr)
|
||||
if (err) {
|
||||
reject(err)
|
||||
return
|
||||
}
|
||||
resolve()
|
||||
})
|
||||
})
|
||||
}
|
55
packages/daemon/src/util/_spawn.ts
Normal file
55
packages/daemon/src/util/_spawn.ts
Normal file
@ -0,0 +1,55 @@
|
||||
import { spawn } from 'child_process'
|
||||
import { existsSync } from 'fs'
|
||||
import { DAEMON_PB_BIN_DIR, DAEMON_PB_DATA_DIR } from '../constants'
|
||||
import { mkInternalAddress, mkInternalUrl } from './internal'
|
||||
import { tryFetch } from './tryFetch'
|
||||
|
||||
export const _spawn = async (cfg: {
|
||||
subdomain: string
|
||||
port: number
|
||||
bin: string
|
||||
cleanup?: (code: number | null) => void
|
||||
}) => {
|
||||
const { subdomain, port, bin, cleanup } = cfg
|
||||
const cmd = `${DAEMON_PB_BIN_DIR}/${bin}`
|
||||
if (!existsSync(cmd)) {
|
||||
throw new Error(
|
||||
`PocketBase binary (${bin}) not found. Contact pockethost.io.`
|
||||
)
|
||||
}
|
||||
|
||||
const args = [
|
||||
`serve`,
|
||||
`--dir`,
|
||||
`${DAEMON_PB_DATA_DIR}/${subdomain}/pb_data`,
|
||||
`--http`,
|
||||
mkInternalAddress(port),
|
||||
]
|
||||
console.log(`Spawning ${subdomain}`, { cmd, args })
|
||||
const ls = spawn(cmd, args)
|
||||
|
||||
ls.stdout.on('data', (data) => {
|
||||
console.log(`${subdomain} stdout: ${data}`)
|
||||
})
|
||||
|
||||
ls.stderr.on('data', (data) => {
|
||||
console.error(`${subdomain} stderr: ${data}`)
|
||||
})
|
||||
|
||||
ls.on('close', (code) => {
|
||||
console.log(`${subdomain} closed with code ${code}`)
|
||||
})
|
||||
ls.on(
|
||||
'exit',
|
||||
cleanup ||
|
||||
((code) => {
|
||||
console.log(`Exited with ${code}`)
|
||||
})
|
||||
)
|
||||
ls.on('error', (err) => {
|
||||
console.log(`${subdomain} had error ${err}`)
|
||||
})
|
||||
|
||||
await tryFetch(mkInternalUrl(port))
|
||||
return ls
|
||||
}
|
3
packages/daemon/src/util/internal.ts
Normal file
3
packages/daemon/src/util/internal.ts
Normal file
@ -0,0 +1,3 @@
|
||||
export const mkInternalAddress = (port: number) => `127.0.0.1:${port}`
|
||||
export const mkInternalUrl = (port: number) =>
|
||||
`http://${mkInternalAddress(port)}`
|
12
packages/daemon/src/util/safeCatch.ts
Normal file
12
packages/daemon/src/util/safeCatch.ts
Normal file
@ -0,0 +1,12 @@
|
||||
export const safeCatch = <TIn extends any[], TOut>(
|
||||
name: string,
|
||||
cb: (...args: TIn) => Promise<TOut>
|
||||
) => {
|
||||
return (...args: TIn) => {
|
||||
console.log(`${name}`)
|
||||
return cb(...args).catch((e: any) => {
|
||||
console.error(`${name} failed: ${e}`)
|
||||
throw e
|
||||
})
|
||||
}
|
||||
}
|
18
packages/daemon/src/util/tryFetch.ts
Normal file
18
packages/daemon/src/util/tryFetch.ts
Normal file
@ -0,0 +1,18 @@
|
||||
import fetch from 'node-fetch'
|
||||
|
||||
export const tryFetch = (url: string) =>
|
||||
new Promise<void>((resolve, reject) => {
|
||||
const tryFetch = () => {
|
||||
console.log(`Trying to connect to instance ${url} `)
|
||||
fetch(url)
|
||||
.then(() => {
|
||||
console.log(`Connection to ${url} successful`)
|
||||
resolve()
|
||||
})
|
||||
.catch((e) => {
|
||||
console.error(`Could not connect to ${url}`)
|
||||
setTimeout(tryFetch, 1000)
|
||||
})
|
||||
}
|
||||
tryFetch()
|
||||
})
|
21
packages/pocketbase/build.sh
Executable file
21
packages/pocketbase/build.sh
Executable file
@ -0,0 +1,21 @@
|
||||
#!/bin/bash
|
||||
|
||||
CGO_ENABLED=0
|
||||
|
||||
SRC=src
|
||||
TARGET=build/$PLATFORM/$VERSION
|
||||
DIST=./dist
|
||||
rm -rf $TARGET
|
||||
mkdir -p $TARGET
|
||||
mkdir -p $DIST
|
||||
cp -r $SRC/* $TARGET
|
||||
echo `pwd`
|
||||
cd $TARGET
|
||||
echo "Building ${BIN}"
|
||||
echo "Fetching pocketbase version $VERSION"
|
||||
go get github.com/pocketbase/pocketbase@v$VERSION
|
||||
echo "Tidying modules"
|
||||
go mod tidy
|
||||
echo "Building ${BIN}"
|
||||
go build -o ../../../$DIST/$BIN
|
||||
echo "Build ${BIN} complete" `date`
|
39
packages/pocketbase/build.ts
Normal file
39
packages/pocketbase/build.ts
Normal file
@ -0,0 +1,39 @@
|
||||
import { binFor, RELEASES } from '@pockethost/common'
|
||||
import { forEach } from '@s-libs/micro-dash'
|
||||
import Bottleneck from 'bottleneck'
|
||||
import { exec } from 'child_process'
|
||||
import Listr from 'listr'
|
||||
|
||||
const limiter = new Bottleneck({ maxConcurrent: 10 })
|
||||
|
||||
const pexec = (cmd: string, cwd = __dirname) => {
|
||||
return new Promise<void>((resolve, reject) => {
|
||||
console.log(cmd)
|
||||
exec(cmd, { cwd }, (err, stdout, stderr) => {
|
||||
console.log(stdout)
|
||||
console.error(stderr)
|
||||
if (err) {
|
||||
reject(err)
|
||||
return
|
||||
}
|
||||
resolve()
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
const tasks = new Listr([], { concurrent: true })
|
||||
forEach(RELEASES, (info, platform) => {
|
||||
forEach(info.versions, (version) => {
|
||||
const cmd = `VERSION=${version} PLATFORM=${platform} BIN=${binFor(
|
||||
platform,
|
||||
version
|
||||
)} ./build.sh`
|
||||
tasks.add({
|
||||
title: `${platform}: ${version}`,
|
||||
task: () => limiter.schedule(() => pexec(cmd)),
|
||||
})
|
||||
})
|
||||
})
|
||||
tasks.run().catch((err) => {
|
||||
console.error(err)
|
||||
})
|
@ -1,16 +1,20 @@
|
||||
{
|
||||
"name": "enchanted-pocketbase",
|
||||
"name": "@pockethost/pocketbase",
|
||||
"version": "0.0.1",
|
||||
"license": "MIT",
|
||||
"scripts": {
|
||||
"build": "cd src && CGO_ENABLED=0 go build -o ../dist/pocketbase && echo 'Build complete' `date`",
|
||||
"build:arm64": "cd src && GOOS=linux GOARCH=arm64 CGO_ENABLED=0 go build -o ../dist/pocketbase && echo 'Build complete' `date`",
|
||||
"build:beta:arm64": "cd src && GOOS=linux GOARCH=arm64 CGO_ENABLED=0 go build -o ../dist/pocketbase-beta && echo 'Build complete' `date`",
|
||||
"build:386": "cd src && GOOS=linux GOARCH=386 CGO_ENABLED=0 go build -o ../dist/pocketbase && echo 'Build complete' `date`",
|
||||
"build:beta:386": "cd src && GOOS=linux GOARCH=386 CGO_ENABLED=0 go build -o ../dist/pocketbase-beta && echo 'Build complete' `date`",
|
||||
"watch:beta:386": "chokidar 'src/**/*' -c 'yarn build:beta:386' --initial"
|
||||
"build": "tsx build.ts"
|
||||
},
|
||||
"devDependencies": {
|
||||
"chokidar-cli": "^3.0.0"
|
||||
"chokidar-cli": "^3.0.0",
|
||||
"tsx": "^3.11.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"@pockethost/common": "0.0.1",
|
||||
"@s-libs/micro-dash": "^14.1.0",
|
||||
"@types/listr": "^0.14.4",
|
||||
"@types/node": "^18.11.9",
|
||||
"bottleneck": "^2.19.5",
|
||||
"listr": "^0.14.3"
|
||||
}
|
||||
}
|
@ -2,31 +2,31 @@ module pocketbase
|
||||
|
||||
go 1.19
|
||||
|
||||
require github.com/pocketbase/pocketbase v0.7.9
|
||||
require github.com/pocketbase/pocketbase v0.8.0-rc1
|
||||
|
||||
require (
|
||||
github.com/AlecAivazis/survey/v2 v2.3.6 // indirect
|
||||
github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d // indirect
|
||||
github.com/aws/aws-sdk-go v1.44.102 // indirect
|
||||
github.com/aws/aws-sdk-go-v2 v1.16.16 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.4.8 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/config v1.17.7 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.12.20 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.12.17 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.11.33 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.23 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.17 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/ini v1.3.24 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/v4a v1.0.14 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.9.9 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.1.18 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.17 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.13.17 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/s3 v1.27.11 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/sso v1.11.23 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.13.5 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.16.19 // indirect
|
||||
github.com/aws/smithy-go v1.13.3 // indirect
|
||||
github.com/aws/aws-sdk-go v1.44.126 // indirect
|
||||
github.com/aws/aws-sdk-go-v2 v1.17.1 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.4.9 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/config v1.17.10 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.12.23 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.12.19 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.11.37 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.25 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.19 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/ini v1.3.26 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/v4a v1.0.16 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.9.10 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.1.20 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.19 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.13.19 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/s3 v1.29.1 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/sso v1.11.25 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.13.8 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.17.1 // indirect
|
||||
github.com/aws/smithy-go v1.13.4 // indirect
|
||||
github.com/disintegration/imaging v1.6.2 // indirect
|
||||
github.com/domodwyer/mailyak/v3 v3.3.4 // indirect
|
||||
github.com/fatih/color v1.13.0 // indirect
|
||||
@ -38,48 +38,48 @@ require (
|
||||
github.com/golang/protobuf v1.5.2 // indirect
|
||||
github.com/google/uuid v1.3.0 // indirect
|
||||
github.com/google/wire v0.5.0 // indirect
|
||||
github.com/googleapis/gax-go/v2 v2.5.1 // indirect
|
||||
github.com/googleapis/gax-go/v2 v2.6.0 // indirect
|
||||
github.com/inconshreveable/mousetrap v1.0.1 // indirect
|
||||
github.com/jmespath/go-jmespath v0.4.0 // indirect
|
||||
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect
|
||||
github.com/labstack/echo/v5 v5.0.0-20220201181537-ed2888cfa198 // indirect
|
||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||
github.com/mattn/go-isatty v0.0.16 // indirect
|
||||
github.com/mattn/go-sqlite3 v1.14.15 // indirect
|
||||
github.com/mattn/go-sqlite3 v1.14.16 // indirect
|
||||
github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d // indirect
|
||||
github.com/pocketbase/dbx v1.6.0 // indirect
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 // indirect
|
||||
github.com/pocketbase/dbx v1.7.0 // indirect
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20220927061507-ef77025ab5aa // indirect
|
||||
github.com/spf13/cast v1.5.0 // indirect
|
||||
github.com/spf13/cobra v1.5.0 // indirect
|
||||
github.com/spf13/cobra v1.6.1 // indirect
|
||||
github.com/spf13/pflag v1.0.5 // indirect
|
||||
github.com/valyala/bytebufferpool v1.0.0 // indirect
|
||||
github.com/valyala/fasttemplate v1.2.1 // indirect
|
||||
github.com/valyala/fasttemplate v1.2.2 // indirect
|
||||
go.opencensus.io v0.23.0 // indirect
|
||||
gocloud.dev v0.26.0 // indirect
|
||||
golang.org/x/crypto v0.0.0-20220919173607-35f4265a4bc0 // indirect
|
||||
golang.org/x/image v0.0.0-20220902085622-e7cb96979f69 // indirect
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 // indirect
|
||||
golang.org/x/net v0.0.0-20220921155015-db77216a4ee9 // indirect
|
||||
golang.org/x/oauth2 v0.0.0-20220909003341-f21342109be1 // indirect
|
||||
golang.org/x/sys v0.0.0-20220919091848-fb04ddd9f9c8 // indirect
|
||||
golang.org/x/term v0.0.0-20220919170432-7a66f970e087 // indirect
|
||||
golang.org/x/text v0.3.7 // indirect
|
||||
golang.org/x/time v0.0.0-20220920022843-2ce7c2934d45 // indirect
|
||||
golang.org/x/tools v0.1.12 // indirect
|
||||
gocloud.dev v0.27.0 // indirect
|
||||
golang.org/x/crypto v0.1.0 // indirect
|
||||
golang.org/x/image v0.1.0 // indirect
|
||||
golang.org/x/mod v0.6.0 // indirect
|
||||
golang.org/x/net v0.1.0 // indirect
|
||||
golang.org/x/oauth2 v0.1.0 // indirect
|
||||
golang.org/x/sys v0.1.0 // indirect
|
||||
golang.org/x/term v0.1.0 // indirect
|
||||
golang.org/x/text v0.4.0 // indirect
|
||||
golang.org/x/time v0.1.0 // indirect
|
||||
golang.org/x/tools v0.2.0 // indirect
|
||||
golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect
|
||||
google.golang.org/api v0.96.0 // indirect
|
||||
google.golang.org/api v0.101.0 // indirect
|
||||
google.golang.org/appengine v1.6.7 // indirect
|
||||
google.golang.org/genproto v0.0.0-20220920201722-2b89144ce006 // indirect
|
||||
google.golang.org/grpc v1.49.0 // indirect
|
||||
google.golang.org/genproto v0.0.0-20221027153422-115e99e71e1c // indirect
|
||||
google.golang.org/grpc v1.50.1 // indirect
|
||||
google.golang.org/protobuf v1.28.1 // indirect
|
||||
lukechampine.com/uint128 v1.2.0 // indirect
|
||||
modernc.org/cc/v3 v3.39.0 // indirect
|
||||
modernc.org/ccgo/v3 v3.16.9 // indirect
|
||||
modernc.org/libc v1.19.0 // indirect
|
||||
modernc.org/cc/v3 v3.40.0 // indirect
|
||||
modernc.org/ccgo/v3 v3.16.13-0.20221017192402-261537637ce8 // indirect
|
||||
modernc.org/libc v1.21.4 // indirect
|
||||
modernc.org/mathutil v1.5.0 // indirect
|
||||
modernc.org/memory v1.4.0 // indirect
|
||||
modernc.org/opt v0.1.3 // indirect
|
||||
modernc.org/sqlite v1.19.1 // indirect
|
||||
modernc.org/sqlite v1.19.3 // indirect
|
||||
modernc.org/strutil v1.1.3 // indirect
|
||||
modernc.org/token v1.0.1 // indirect
|
||||
)
|
||||
|
File diff suppressed because it is too large
Load Diff
Binary file not shown.
103
packages/pocketbase/tsconfig.json
Normal file
103
packages/pocketbase/tsconfig.json
Normal file
@ -0,0 +1,103 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
/* Visit https://aka.ms/tsconfig to read more about this file */
|
||||
|
||||
/* Projects */
|
||||
// "incremental": true, /* Save .tsbuildinfo files to allow for incremental compilation of projects. */
|
||||
// "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */
|
||||
// "tsBuildInfoFile": "./.tsbuildinfo", /* Specify the path to .tsbuildinfo incremental compilation file. */
|
||||
// "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects. */
|
||||
// "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */
|
||||
// "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */
|
||||
|
||||
/* Language and Environment */
|
||||
"target": "es2016" /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */,
|
||||
// "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */
|
||||
// "jsx": "preserve", /* Specify what JSX code is generated. */
|
||||
// "experimentalDecorators": true, /* Enable experimental support for TC39 stage 2 draft decorators. */
|
||||
// "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */
|
||||
// "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h'. */
|
||||
// "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */
|
||||
// "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using 'jsx: react-jsx*'. */
|
||||
// "reactNamespace": "", /* Specify the object invoked for 'createElement'. This only applies when targeting 'react' JSX emit. */
|
||||
// "noLib": true, /* Disable including any library files, including the default lib.d.ts. */
|
||||
// "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */
|
||||
// "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */
|
||||
|
||||
/* Modules */
|
||||
"module": "commonjs" /* Specify what module code is generated. */,
|
||||
// "rootDir": "./", /* Specify the root folder within your source files. */
|
||||
// "moduleResolution": "node", /* Specify how TypeScript looks up a file from a given module specifier. */
|
||||
// "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */
|
||||
// "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */
|
||||
// "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */
|
||||
// "typeRoots": [], /* Specify multiple folders that act like './node_modules/@types'. */
|
||||
// "types": [], /* Specify type package names to be included without being referenced in a source file. */
|
||||
// "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */
|
||||
// "moduleSuffixes": [], /* List of file name suffixes to search when resolving a module. */
|
||||
"resolveJsonModule": true /* Enable importing .json files. */,
|
||||
// "noResolve": true, /* Disallow 'import's, 'require's or '<reference>'s from expanding the number of files TypeScript should add to a project. */
|
||||
|
||||
/* JavaScript Support */
|
||||
// "allowJs": true, /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */
|
||||
// "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */
|
||||
// "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from 'node_modules'. Only applicable with 'allowJs'. */
|
||||
|
||||
/* Emit */
|
||||
// "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */
|
||||
// "declarationMap": true, /* Create sourcemaps for d.ts files. */
|
||||
// "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */
|
||||
// "sourceMap": true, /* Create source map files for emitted JavaScript files. */
|
||||
// "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If 'declaration' is true, also designates a file that bundles all .d.ts output. */
|
||||
// "outDir": "./", /* Specify an output folder for all emitted files. */
|
||||
// "removeComments": true, /* Disable emitting comments. */
|
||||
// "noEmit": true, /* Disable emitting files from a compilation. */
|
||||
// "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */
|
||||
// "importsNotUsedAsValues": "remove", /* Specify emit/checking behavior for imports that are only used for types. */
|
||||
// "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */
|
||||
// "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */
|
||||
// "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */
|
||||
// "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */
|
||||
// "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */
|
||||
// "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */
|
||||
// "newLine": "crlf", /* Set the newline character for emitting files. */
|
||||
// "stripInternal": true, /* Disable emitting declarations that have '@internal' in their JSDoc comments. */
|
||||
// "noEmitHelpers": true, /* Disable generating custom helper functions like '__extends' in compiled output. */
|
||||
// "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */
|
||||
// "preserveConstEnums": true, /* Disable erasing 'const enum' declarations in generated code. */
|
||||
// "declarationDir": "./", /* Specify the output directory for generated declaration files. */
|
||||
// "preserveValueImports": true, /* Preserve unused imported values in the JavaScript output that would otherwise be removed. */
|
||||
|
||||
/* Interop Constraints */
|
||||
// "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */
|
||||
// "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */
|
||||
"esModuleInterop": true /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */,
|
||||
// "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */
|
||||
"forceConsistentCasingInFileNames": true /* Ensure that casing is correct in imports. */,
|
||||
|
||||
/* Type Checking */
|
||||
"strict": true /* Enable all strict type-checking options. */,
|
||||
// "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied 'any' type. */
|
||||
// "strictNullChecks": true, /* When type checking, take into account 'null' and 'undefined'. */
|
||||
// "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */
|
||||
// "strictBindCallApply": true, /* Check that the arguments for 'bind', 'call', and 'apply' methods match the original function. */
|
||||
// "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */
|
||||
// "noImplicitThis": true, /* Enable error reporting when 'this' is given the type 'any'. */
|
||||
// "useUnknownInCatchVariables": true, /* Default catch clause variables as 'unknown' instead of 'any'. */
|
||||
// "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */
|
||||
// "noUnusedLocals": true, /* Enable error reporting when local variables aren't read. */
|
||||
// "noUnusedParameters": true, /* Raise an error when a function parameter isn't read. */
|
||||
// "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */
|
||||
// "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */
|
||||
// "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */
|
||||
// "noUncheckedIndexedAccess": true, /* Add 'undefined' to a type when accessed using an index. */
|
||||
// "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */
|
||||
// "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type. */
|
||||
// "allowUnusedLabels": true, /* Disable error reporting for unused labels. */
|
||||
// "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */
|
||||
|
||||
/* Completeness */
|
||||
// "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */
|
||||
"skipLibCheck": true /* Skip type checking all .d.ts files. */
|
||||
}
|
||||
}
|
@ -3,7 +3,7 @@
|
||||
"version": "0.0.1",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "vite dev",
|
||||
"dev": "vite dev --force",
|
||||
"build": "vite build",
|
||||
"preview": "vite preview",
|
||||
"check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
|
||||
@ -30,7 +30,7 @@
|
||||
"@s-libs/micro-dash": "12",
|
||||
"@types/js-cookie": "^3.0.2",
|
||||
"js-cookie": "^3.0.1",
|
||||
"pocketbase": "^0.7.0",
|
||||
"pocketbase": "^0.8.0-rc1",
|
||||
"random-word-slugs": "^0.1.6",
|
||||
"sass": "^1.54.9",
|
||||
"svelte-highlight": "^6.2.1"
|
||||
|
@ -3,11 +3,13 @@ import {
|
||||
assertExists,
|
||||
createRealtimeSubscriptionManager,
|
||||
type InstanceId,
|
||||
type Instance_In,
|
||||
type Instance_Out
|
||||
type InstancesRecord,
|
||||
type InstancesRecord_New,
|
||||
type RealtimeEventHandler,
|
||||
type UserRecord
|
||||
} from '@pockethost/common'
|
||||
import { keys, map } from '@s-libs/micro-dash'
|
||||
import PocketBase, { Admin, BaseAuthStore, ClientResponseError, Record, User } from 'pocketbase'
|
||||
import PocketBase, { Admin, BaseAuthStore, ClientResponseError, Record } from 'pocketbase'
|
||||
import type { Unsubscriber } from 'svelte/store'
|
||||
import { safeCatch } from '../util/safeCatch'
|
||||
|
||||
@ -16,7 +18,7 @@ export type AuthChangeHandler = (user: BaseAuthStore) => void
|
||||
export type AuthToken = string
|
||||
export type AuthStoreProps = {
|
||||
token: AuthToken
|
||||
model: User | null
|
||||
model: UserRecord | null
|
||||
isValid: boolean
|
||||
}
|
||||
|
||||
@ -27,66 +29,76 @@ export const createPocketbaseClient = (url: string) => {
|
||||
|
||||
const { authStore } = client
|
||||
|
||||
const user = () => authStore.model
|
||||
const user = () => authStore.model as AuthStoreProps['model']
|
||||
|
||||
const isLoggedIn = () => authStore.isValid
|
||||
|
||||
const logOut = () => authStore.clear()
|
||||
|
||||
const createUser = safeCatch(`createUser`, (email: string, password: string) =>
|
||||
client.users.create({
|
||||
email,
|
||||
password,
|
||||
passwordConfirm: password
|
||||
})
|
||||
client
|
||||
.collection('users')
|
||||
.create({
|
||||
email,
|
||||
password,
|
||||
passwordConfirm: password
|
||||
})
|
||||
.then(() => {
|
||||
console.log(`Sending verification email to ${email}`)
|
||||
return client.collection('users').requestVerification(email)
|
||||
})
|
||||
)
|
||||
|
||||
const authViaEmail = safeCatch(`authViaEmail`, (email: string, password: string) =>
|
||||
client.users.authViaEmail(email, password)
|
||||
client.collection('users').authWithPassword(email, password)
|
||||
)
|
||||
|
||||
const refreshAuthToken = safeCatch(`refreshAuthToken`, () => client.users.refresh())
|
||||
const refreshAuthToken = safeCatch(`refreshAuthToken`, () =>
|
||||
client.collection('users').authRefresh()
|
||||
)
|
||||
|
||||
const createInstance = safeCatch(
|
||||
`createInstance`,
|
||||
(payload: Instance_In): Promise<Instance_Out> => {
|
||||
return client.records.create('instances', payload).then((r) => r as unknown as Instance_Out)
|
||||
(payload: InstancesRecord_New): Promise<InstancesRecord> => {
|
||||
return client.collection('instances').create<InstancesRecord>(payload)
|
||||
}
|
||||
)
|
||||
|
||||
const getInstanceById = safeCatch(
|
||||
`getInstanceById`,
|
||||
(id: InstanceId): Promise<Instance_Out | undefined> =>
|
||||
client.records.getOne('instances', id).then((r) => r as unknown as Instance_Out)
|
||||
(id: InstanceId): Promise<InstancesRecord | undefined> =>
|
||||
client.collection('instances').getOne<InstancesRecord>(id)
|
||||
)
|
||||
|
||||
const subscribe = createRealtimeSubscriptionManager(client)
|
||||
const { subscribeOne } = createRealtimeSubscriptionManager(client)
|
||||
|
||||
const watchInstanceById = (id: InstanceId, cb: (rec: Instance_Out) => void): Unsubscriber => {
|
||||
const slug = `instances/${id}`
|
||||
getInstanceById(id).then((v) => {
|
||||
if (!v) return
|
||||
cb(v)
|
||||
const watchInstanceById = (
|
||||
id: InstanceId,
|
||||
cb: RealtimeEventHandler<InstancesRecord>
|
||||
): Unsubscriber => {
|
||||
getInstanceById(id).then((record) => {
|
||||
console.log(`Got instnace`, record)
|
||||
assertExists(record, `Expected instance ${id} here`)
|
||||
cb({ action: 'init', record })
|
||||
})
|
||||
return subscribe(slug, cb)
|
||||
return subscribeOne('instances', id, cb)
|
||||
}
|
||||
|
||||
const getAllInstancesById = safeCatch(`getAllInstancesById`, async () =>
|
||||
(
|
||||
await client.records.getFullList('instances').catch((e) => {
|
||||
console.error(`getAllInstancesById failed with ${e}`)
|
||||
throw e
|
||||
})
|
||||
await client
|
||||
.collection('instances')
|
||||
.getFullList()
|
||||
.catch((e) => {
|
||||
console.error(`getAllInstancesById failed with ${e}`)
|
||||
throw e
|
||||
})
|
||||
).reduce((c, v) => {
|
||||
c[v.id] = v
|
||||
return c
|
||||
}, {} as Record)
|
||||
)
|
||||
|
||||
const setInstance = safeCatch(`setInstance`, (instanceId: InstanceId, fields: Instance_In) => {
|
||||
return client.records.update('instances', instanceId, fields)
|
||||
})
|
||||
|
||||
const parseError = (e: Error): string[] => {
|
||||
if (!(e instanceof ClientResponseError)) return [e.message]
|
||||
if (e.data.message && keys(e.data.data).length === 0) return [e.data.message]
|
||||
@ -96,13 +108,14 @@ export const createPocketbaseClient = (url: string) => {
|
||||
const resendVerificationEmail = safeCatch(`resendVerificationEmail`, async () => {
|
||||
const user = client.authStore.model
|
||||
assertExists(user, `Login required`)
|
||||
await client.users.requestVerification(user.email)
|
||||
await client.collection('users').requestVerification(user.email)
|
||||
})
|
||||
|
||||
const getAuthStoreProps = (): AuthStoreProps => {
|
||||
const { token, model, isValid } = client.authStore
|
||||
const { token, model, isValid } = client.authStore as AuthStoreProps
|
||||
// console.log(`curent authstore`, { token, model, isValid })
|
||||
if (model instanceof Admin) throw new Error(`Admin models not supported`)
|
||||
if (model && !model.email) throw new Error(`Expected model to be a user here`)
|
||||
return {
|
||||
token,
|
||||
model,
|
||||
@ -132,7 +145,10 @@ export const createPocketbaseClient = (url: string) => {
|
||||
* out of date, or fields in the user record may have changed in the backend.
|
||||
*/
|
||||
refreshAuthToken()
|
||||
.catch(() => {})
|
||||
.catch((e) => {
|
||||
console.error(`Clearing auth store: ${e}`)
|
||||
client.authStore.clear()
|
||||
})
|
||||
.finally(() => {
|
||||
fireAuthChange(getAuthStoreProps())
|
||||
})
|
||||
@ -154,7 +170,7 @@ export const createPocketbaseClient = (url: string) => {
|
||||
unsub()
|
||||
return
|
||||
}
|
||||
const _check = safeCatch(`_checkVerified`, () => client.users.refresh())
|
||||
const _check = safeCatch(`_checkVerified`, refreshAuthToken)
|
||||
setTimeout(_check, 1000)
|
||||
|
||||
// FIXME - THIS DOES NOT WORK, WE HAVE TO POLL INSTEAD. FIX IN V0.8
|
||||
@ -169,7 +185,6 @@ export const createPocketbaseClient = (url: string) => {
|
||||
return {
|
||||
getAuthStoreProps,
|
||||
parseError,
|
||||
subscribe,
|
||||
getInstanceById,
|
||||
createInstance,
|
||||
authViaEmail,
|
||||
@ -180,7 +195,6 @@ export const createPocketbaseClient = (url: string) => {
|
||||
user,
|
||||
watchInstanceById,
|
||||
getAllInstancesById,
|
||||
setInstance,
|
||||
resendVerificationEmail
|
||||
}
|
||||
}
|
||||
|
@ -5,25 +5,27 @@
|
||||
import { PUBLIC_PB_PROTOCOL } from '$env/static/public'
|
||||
import { PUBLIC_PB_DOMAIN } from '$src/env'
|
||||
import { client } from '$src/pocketbase'
|
||||
import { humanVersion, type InstancesRecord } from '@pockethost/common'
|
||||
import { assertExists } from '@pockethost/common/src/assert'
|
||||
import type { Instance_Out } from '@pockethost/common/src/schema'
|
||||
import { onDestroy, onMount } from 'svelte'
|
||||
import type { Unsubscriber } from 'svelte/store'
|
||||
|
||||
const { instanceId } = $page.params
|
||||
|
||||
let instance: Instance_Out | undefined
|
||||
let instance: InstancesRecord | undefined
|
||||
|
||||
let url: string
|
||||
let code: string = ''
|
||||
let unsub: Unsubscriber = () => {}
|
||||
|
||||
onMount(() => {
|
||||
onMount(async () => {
|
||||
const { watchInstanceById } = client()
|
||||
unsub = watchInstanceById(instanceId, (r) => {
|
||||
instance = r
|
||||
assertExists(instance, `Expected instance here`)
|
||||
const { subdomain } = instance
|
||||
console.log(`Handling instance update`, r)
|
||||
const { action, record } = r
|
||||
instance = record
|
||||
assertExists(record, `Expected instance here`)
|
||||
const { subdomain } = record
|
||||
url = `${PUBLIC_PB_PROTOCOL}://${subdomain}.${PUBLIC_PB_DOMAIN}`
|
||||
code = `const url = '${url}'\nconst client = new PocketBase(url)`
|
||||
})
|
||||
@ -50,6 +52,10 @@
|
||||
JavaScript:
|
||||
<CodeSample {code} />
|
||||
</div>
|
||||
<div>
|
||||
Running {instance.platform}
|
||||
{humanVersion(instance.platform, instance.version)}
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<div class="text-center py-5">
|
||||
|
@ -5,7 +5,7 @@
|
||||
import { PUBLIC_PB_DOMAIN } from '$src/env'
|
||||
import { client } from '$src/pocketbase'
|
||||
import { createCleanupManagerSync } from '$util/CleanupManager'
|
||||
import type { Instance_Out, Instance_Out_ByIdCollection } from '@pockethost/common/src/schema'
|
||||
import { humanVersion, type InstanceRecordById, type InstancesRecord } from '@pockethost/common'
|
||||
import { forEach, values } from '@s-libs/micro-dash'
|
||||
import { onDestroy, onMount } from 'svelte'
|
||||
import { fade } from 'svelte/transition'
|
||||
@ -13,18 +13,18 @@
|
||||
// Wait for the instance call to complete before rendering the UI
|
||||
let hasPageLoaded = false
|
||||
|
||||
let apps: Instance_Out_ByIdCollection = {}
|
||||
let apps: InstanceRecordById = {}
|
||||
|
||||
// This will update when the `apps` value changes
|
||||
$: isFirstApplication = values(apps).length === 0
|
||||
|
||||
let appsArray: Instance_Out[]
|
||||
let appsArray: InstancesRecord[]
|
||||
$: {
|
||||
appsArray = values(apps)
|
||||
}
|
||||
const cm = createCleanupManagerSync()
|
||||
let _touch = 0 // This is a fake var because without it the watcher callback will not update UI when the apps object changes
|
||||
const _update = (_apps: Instance_Out_ByIdCollection) => {
|
||||
const _update = (_apps: InstanceRecordById) => {
|
||||
apps = _apps
|
||||
_touch++
|
||||
}
|
||||
@ -38,7 +38,8 @@
|
||||
const instanceId = app.id
|
||||
|
||||
const unsub = watchInstanceById(instanceId, (r) => {
|
||||
_update({ ...apps, [r.id]: r })
|
||||
const { action, record } = r
|
||||
_update({ ...apps, [record.id]: record })
|
||||
})
|
||||
cm.add(unsub)
|
||||
})
|
||||
@ -74,6 +75,8 @@
|
||||
</div>
|
||||
|
||||
<h2 class="mb-4 font-monospace">{app.subdomain}</h2>
|
||||
Running {app.platform}
|
||||
{humanVersion(app.platform, app.version)}
|
||||
|
||||
<div class="d-flex justify-content-around">
|
||||
<a href={`/app/instances/${app.id}`} class="btn btn-light">
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { goto } from '$app/navigation'
|
||||
import { client } from '$src/pocketbase'
|
||||
import { InstanceStatus } from '@pockethost/common'
|
||||
import { InstanceStatus, LATEST_PLATFORM, USE_LATEST_VERSION } from '@pockethost/common'
|
||||
|
||||
export type FormErrorHandler = (value: string) => void
|
||||
|
||||
@ -83,7 +83,9 @@ export const handleCreateNewInstance = async (
|
||||
const record = await createInstance({
|
||||
subdomain: instanceName,
|
||||
uid: id,
|
||||
status: InstanceStatus.Idle
|
||||
status: InstanceStatus.Idle,
|
||||
platform: LATEST_PLATFORM,
|
||||
version: USE_LATEST_VERSION
|
||||
})
|
||||
|
||||
await goto(`/app/instances/${record.id}`)
|
||||
|
18
readme.md
18
readme.md
@ -93,9 +93,9 @@ git clone git@github.com:benallfree/pockethost.git
|
||||
cd pockethost/docker
|
||||
cp .env-template-dev .env.local # Edit as needed - defaults should work
|
||||
cd ..
|
||||
docker-compose -f docker/docker-compose-dev.yaml build
|
||||
docker-compose -f docker/docker-compose-dev.yaml --profile=build up --remove-orphans
|
||||
docker-compose -f docker/docker-compose-dev.yaml --profile=serve up --remove-orphans
|
||||
docker-compose -f docker/build.yaml up --remove-orphans
|
||||
docker-compose -f docker/migrate.yaml up --remove-orphans
|
||||
docker-compose -f docker/dev.yaml up --remove-orphans
|
||||
open https://pockethost.test
|
||||
```
|
||||
|
||||
@ -108,10 +108,9 @@ git clone git@github.com:benallfree/pockethost.git
|
||||
cd pockethost/docker
|
||||
cp .env-template-prod .env.local # Edit as needed - defaults should work
|
||||
cd ..
|
||||
docker-compose -f docker/docker-compose-prod.yaml build
|
||||
# Use 'buildbox' to test your build before launching service
|
||||
docker-compose -f docker/docker-compose-prod.yaml --profile=build up --remove-orphans
|
||||
docker-compose -f docker/docker-compose-prod.yaml --profile=serve up --remove-orphans
|
||||
docker-compose -f docker/build.yaml up --remove-orphans
|
||||
docker-compose -f docker/migrate.yaml up --remove-orphans
|
||||
docker-compose -f docker/prod.yaml up --remove-orphans
|
||||
```
|
||||
|
||||
**2. Refresh Certbot**
|
||||
@ -131,9 +130,10 @@ open https://pockethost.io
|
||||
|
||||
# Release History
|
||||
|
||||
**next**
|
||||
**0.4.0**
|
||||
|
||||
- [ ]
|
||||
- [x] PocketBase 0.8 support
|
||||
- [x] Introduced "platforms" concept for version control
|
||||
|
||||
**0.3.2**
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user