mirror of
https://github.com/pockethost/pockethost.git
synced 2025-03-30 15:08:30 +00:00
Merge branch 'master' of github.com:benallfree/pockethost
This commit is contained in:
commit
a3cc606061
@ -8,3 +8,4 @@ build
|
|||||||
*.njk
|
*.njk
|
||||||
_site
|
_site
|
||||||
forks
|
forks
|
||||||
|
src/mothership-app/pb_hooks/src/versions.pb.js
|
@ -9,6 +9,7 @@
|
|||||||
"check:types:pockethost": "tsc --noEmit --skipLibCheck",
|
"check:types:pockethost": "tsc --noEmit --skipLibCheck",
|
||||||
"lint": "prettier -c \"./**/*.{ts,js,cjs,svelte,json}\"",
|
"lint": "prettier -c \"./**/*.{ts,js,cjs,svelte,json}\"",
|
||||||
"lint:fix": "prettier -w \"./**/*.{ts,js,cjs,svelte,json}\"",
|
"lint:fix": "prettier -w \"./**/*.{ts,js,cjs,svelte,json}\"",
|
||||||
|
"download-versions": "tsx ./src/cli/download.ts",
|
||||||
"build": "concurrently 'pnpm:build:*'",
|
"build": "concurrently 'pnpm:build:*'",
|
||||||
"build-pockethost": "concurrently 'pnpm:build:pockethost:*'",
|
"build-pockethost": "concurrently 'pnpm:build:pockethost:*'",
|
||||||
"build-frontends": "concurrently 'pnpm:build:frontend:*'",
|
"build-frontends": "concurrently 'pnpm:build:frontend:*'",
|
||||||
|
@ -1,24 +1,21 @@
|
|||||||
import { DEBUG, PH_BIN_CACHE } from '$constants'
|
import {
|
||||||
|
DEBUG,
|
||||||
|
DefaultSettingsService,
|
||||||
|
PH_BIN_CACHE,
|
||||||
|
SETTINGS,
|
||||||
|
} from '$constants'
|
||||||
import { PocketbaseReleaseDownloadService } from '$services'
|
import { PocketbaseReleaseDownloadService } from '$services'
|
||||||
import { LogLevelName, LoggerService } from '$shared'
|
import { LogLevelName, LoggerService } from '$shared'
|
||||||
// gen:import
|
|
||||||
|
|
||||||
const [major, minor, patch] = process.versions.node.split('.').map(Number)
|
const check = async () => {
|
||||||
|
DefaultSettingsService(SETTINGS)
|
||||||
|
LoggerService({
|
||||||
|
level: DEBUG() ? LogLevelName.Debug : LogLevelName.Info,
|
||||||
|
errorTrace: !DEBUG(),
|
||||||
|
})
|
||||||
|
|
||||||
if ((major || 0) < 18) {
|
|
||||||
throw new Error(`Node 18 or higher required.`)
|
|
||||||
}
|
|
||||||
|
|
||||||
LoggerService({
|
|
||||||
level: DEBUG() ? LogLevelName.Debug : LogLevelName.Info,
|
|
||||||
errorTrace: !DEBUG(),
|
|
||||||
})
|
|
||||||
|
|
||||||
// npm install eventsource --save
|
|
||||||
// @ts-ignore
|
|
||||||
global.EventSource = require('eventsource')
|
|
||||||
;(async () => {
|
|
||||||
const logger = LoggerService().create(`download.ts`)
|
const logger = LoggerService().create(`download.ts`)
|
||||||
|
|
||||||
const { dbg, error, info, warn } = logger
|
const { dbg, error, info, warn } = logger
|
||||||
info(`Starting`)
|
info(`Starting`)
|
||||||
|
|
||||||
@ -26,4 +23,6 @@ global.EventSource = require('eventsource')
|
|||||||
cachePath: PH_BIN_CACHE(),
|
cachePath: PH_BIN_CACHE(),
|
||||||
})
|
})
|
||||||
await check()
|
await check()
|
||||||
})()
|
}
|
||||||
|
|
||||||
|
check()
|
||||||
|
14
src/mothership-app/pb_hooks/src/versions-route.pb.js
Normal file
14
src/mothership-app/pb_hooks/src/versions-route.pb.js
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
/// <reference path="../types/types.d.ts" />
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return a list of available PocketBase versions
|
||||||
|
*/
|
||||||
|
routerAdd(
|
||||||
|
'GET',
|
||||||
|
'/api/versions',
|
||||||
|
(c) => {
|
||||||
|
const { versions } = require(`${__hooks}/versions.pb.js`)
|
||||||
|
|
||||||
|
return c.json(200, { versions })
|
||||||
|
} /* optional middlewares */,
|
||||||
|
)
|
1
src/mothership-app/pb_hooks/src/versions.pb.js
Normal file
1
src/mothership-app/pb_hooks/src/versions.pb.js
Normal file
@ -0,0 +1 @@
|
|||||||
|
module.exports = {"versions":["0.19.*","0.19.2","0.19.1","0.19.0","0.18.*","0.18.10","0.18.9","0.18.8","0.18.7","0.18.6","0.18.5","0.18.4","0.18.3","0.18.2","0.18.1","0.18.0","0.17.*","0.17.7","0.17.6","0.17.5","0.17.4","0.17.3","0.17.2","0.17.1","0.17.0","0.16.*","0.16.10","0.16.9","0.16.8","0.16.7","0.16.6","0.16.5","0.16.4","0.16.3","0.16.2","0.16.1","0.16.0","0.15.*","0.15.3","0.15.2","0.15.1","0.15.0","0.14.*","0.14.5","0.14.4","0.14.3","0.14.2","0.14.1","0.14.0","0.13.*","0.13.4","0.13.3","0.13.2","0.13.1","0.13.0","0.12.*","0.12.3","0.12.2","0.12.1","0.12.0","0.11.*","0.11.4","0.11.3","0.11.2","0.11.1","0.11.0","0.10.*","0.10.4","0.10.3","0.10.2","0.10.1","0.10.0","0.9.*","0.9.2","0.9.1","0.9.0","0.8.*","0.8.0","0.7.*","0.7.10","0.7.9","0.7.8","0.7.7","0.7.6","0.7.5","0.7.4","0.7.3","0.7.2","0.7.1","0.7.0","0.6.*","0.6.0","0.5.*","0.5.2","0.5.1","0.5.0","0.4.*","0.4.2","0.4.1","0.4.0","0.3.*","0.3.4","0.3.3","0.3.2","0.3.1","0.3.0","0.2.*","0.2.8","0.2.7","0.2.6","0.2.5","0.2.4","0.2.3"]}
|
@ -0,0 +1,46 @@
|
|||||||
|
function compareSemVer(a: string, b: string): number {
|
||||||
|
// Consider wildcards as higher than any version number, hence represented by a large number for comparison
|
||||||
|
let splitA = a
|
||||||
|
.split('.')
|
||||||
|
.map((x) => (x === '*' ? Number.MAX_SAFE_INTEGER : parseInt(x)))
|
||||||
|
let splitB = b
|
||||||
|
.split('.')
|
||||||
|
.map((x) => (x === '*' ? Number.MAX_SAFE_INTEGER : parseInt(x)))
|
||||||
|
|
||||||
|
// Compare each part starting from major, minor, then patch
|
||||||
|
for (let i = 0; i < 3; i++) {
|
||||||
|
if (splitA[i] !== splitB[i]) {
|
||||||
|
return splitB[i]! - splitA[i]! // For descending order, compare b - a
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If all parts are equal or both have wildcards
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
export function expandAndSortSemVers(semvers: string[]): string[] {
|
||||||
|
let expandedVersions = new Set<string>() // Use a set to avoid duplicates
|
||||||
|
|
||||||
|
// Helper function to add wildcard versions
|
||||||
|
const addWildcards = (version: string) => {
|
||||||
|
const parts = version.split('.')
|
||||||
|
|
||||||
|
if (parts.length === 3) {
|
||||||
|
if (parts[0] !== '0') expandedVersions.add(`${parts[0]}.*.*`)
|
||||||
|
expandedVersions.add(`${parts[0]}.${parts[1]}.*`)
|
||||||
|
if (parts[0] === '0' && parts[1] !== '0')
|
||||||
|
expandedVersions.add(`0.${parts[1]}.*`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add wildcards and original versions to the set
|
||||||
|
semvers.forEach((version) => {
|
||||||
|
expandedVersions.add(version)
|
||||||
|
addWildcards(version)
|
||||||
|
})
|
||||||
|
|
||||||
|
// Add the global wildcard for the latest version
|
||||||
|
// expandedVersions.add('*')
|
||||||
|
// Convert the set to an array and sort it using the custom semver comparison function
|
||||||
|
return Array.from(expandedVersions).sort(compareSemVer)
|
||||||
|
}
|
@ -1,11 +1,12 @@
|
|||||||
import { PH_BIN_CACHE } from '$constants'
|
import { MOTHERSHIP_HOOKS_DIR, PH_BIN_CACHE } from '$constants'
|
||||||
import { LoggerService, mkSingleton, SingletonBaseConfig } from '$shared'
|
import { LoggerService, SingletonBaseConfig, mkSingleton } from '$shared'
|
||||||
import { downloadAndExtract, mergeConfig, smartFetch } from '$util'
|
import { downloadAndExtract, mergeConfig, smartFetch } from '$util'
|
||||||
import { keys } from '@s-libs/micro-dash'
|
import { keys } from '@s-libs/micro-dash'
|
||||||
import Bottleneck from 'bottleneck'
|
import Bottleneck from 'bottleneck'
|
||||||
import { chmodSync, existsSync } from 'fs'
|
import { chmodSync, existsSync, writeFileSync } from 'fs'
|
||||||
import { join } from 'path'
|
import { join } from 'path'
|
||||||
import { rsort } from 'semver'
|
import { rsort } from 'semver'
|
||||||
|
import { expandAndSortSemVers } from './expandAndSortSemVers'
|
||||||
|
|
||||||
type Release = {
|
type Release = {
|
||||||
url: string
|
url: string
|
||||||
@ -100,6 +101,15 @@ export const PocketbaseReleaseDownloadService = mkSingleton(
|
|||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
await Promise.all(promises)
|
await Promise.all(promises)
|
||||||
|
|
||||||
|
console.log(`***keys`, keys(binPaths))
|
||||||
|
const sortedSemVers = expandAndSortSemVers(keys(binPaths))
|
||||||
|
writeFileSync(
|
||||||
|
join(MOTHERSHIP_HOOKS_DIR(), `versions.pb.js`),
|
||||||
|
`module.exports = ${JSON.stringify({ versions: sortedSemVers })}`,
|
||||||
|
)
|
||||||
|
console.log(JSON.stringify(sortedSemVers))
|
||||||
|
|
||||||
if (keys(binPaths).length === 0) {
|
if (keys(binPaths).length === 0) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`No version found, probably mismatched architecture and OS (${osName}/${cpuArchitecture})`,
|
`No version found, probably mismatched architecture and OS (${osName}/${cpuArchitecture})`,
|
||||||
|
@ -2,120 +2,100 @@ import { mkSingleton } from '$shared'
|
|||||||
import { boolean as castToBoolean } from 'boolean'
|
import { boolean as castToBoolean } from 'boolean'
|
||||||
import { existsSync, mkdirSync } from 'fs'
|
import { existsSync, mkdirSync } from 'fs'
|
||||||
|
|
||||||
export type HandlerFactory<TValue> = (key: string) => {
|
export type Caster<TValue, TConfig = {}> = {
|
||||||
|
stringToType: (value: string, config?: Partial<TConfig>) => TValue
|
||||||
|
typeToString: (value: TValue, config?: Partial<TConfig>) => string
|
||||||
|
}
|
||||||
|
|
||||||
|
export type Handler<TValue> = {
|
||||||
get: () => TValue
|
get: () => TValue
|
||||||
set: (value: TValue) => void
|
set: (value: TValue) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type HandlerFactory<TValue> = (key: string) => Handler<TValue>
|
||||||
|
|
||||||
export type Maker<TValue, TConfig = {}> = (
|
export type Maker<TValue, TConfig = {}> = (
|
||||||
_default?: TValue,
|
_default?: TValue,
|
||||||
config?: Partial<TConfig>,
|
config?: Partial<TConfig>,
|
||||||
) => HandlerFactory<TValue>
|
) => HandlerFactory<TValue>
|
||||||
|
|
||||||
export const mkBoolean: Maker<boolean> = (_default) => (name: string) => {
|
const mkMaker =
|
||||||
return {
|
<TValue, TConfig = {}>(
|
||||||
get() {
|
caster: Caster<TValue, TConfig>,
|
||||||
const v = process.env[name]
|
): Maker<TValue, TConfig> =>
|
||||||
if (typeof v === `undefined`) {
|
(_default, config) =>
|
||||||
if (typeof _default === `undefined`)
|
|
||||||
throw new Error(`${name} must be defined`)
|
|
||||||
return _default
|
|
||||||
}
|
|
||||||
return castToBoolean(v)
|
|
||||||
},
|
|
||||||
set(v) {
|
|
||||||
process.env[name] = `${v}`
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export const mkNumber: Maker<number> = (_default) => (name: string) => {
|
|
||||||
return {
|
|
||||||
get() {
|
|
||||||
const v = process.env[name]
|
|
||||||
if (typeof v === `undefined`) {
|
|
||||||
if (typeof _default === `undefined`)
|
|
||||||
throw new Error(`${name} must be defined`)
|
|
||||||
return _default
|
|
||||||
}
|
|
||||||
return parseInt(v, 10)
|
|
||||||
},
|
|
||||||
set(v) {
|
|
||||||
process.env[name] = v.toString()
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export const mkPath: Maker<string, { create: boolean; required: boolean }> =
|
|
||||||
(_default, options = {}) =>
|
|
||||||
(name: string) => {
|
(name: string) => {
|
||||||
const { create = false, required = true } = options
|
|
||||||
return {
|
return {
|
||||||
get() {
|
get(): TValue {
|
||||||
const v = (() => {
|
|
||||||
const v = process.env[name]
|
|
||||||
if (typeof v === `undefined`) {
|
|
||||||
if (typeof _default === `undefined`)
|
|
||||||
throw new Error(`${name} must be defined`)
|
|
||||||
return _default
|
|
||||||
}
|
|
||||||
return v
|
|
||||||
})()
|
|
||||||
if (create) {
|
|
||||||
mkdirSync(v, { recursive: true })
|
|
||||||
}
|
|
||||||
if (required && !existsSync(v)) {
|
|
||||||
throw new Error(`${name} (${v}) must exist.`)
|
|
||||||
}
|
|
||||||
return v
|
|
||||||
},
|
|
||||||
set(v) {
|
|
||||||
if (!existsSync(v)) {
|
|
||||||
throw new Error(`${name} (${v}) must exist.`)
|
|
||||||
}
|
|
||||||
process.env[name] = v
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export const mkString: Maker<string> = (_default) => (name: string) => {
|
|
||||||
return {
|
|
||||||
get() {
|
|
||||||
const v = process.env[name]
|
|
||||||
if (typeof v === `undefined`) {
|
|
||||||
if (typeof _default === `undefined`)
|
|
||||||
throw new Error(`${name} must be defined`)
|
|
||||||
return _default
|
|
||||||
}
|
|
||||||
return v
|
|
||||||
},
|
|
||||||
set(v) {
|
|
||||||
process.env[name] = v
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export const mkCsvString: Maker<string[]> = (_default) => (name: string) => {
|
|
||||||
return {
|
|
||||||
get() {
|
|
||||||
return (() => {
|
|
||||||
const v = process.env[name]
|
const v = process.env[name]
|
||||||
if (typeof v === `undefined`) {
|
if (typeof v === `undefined`) {
|
||||||
if (typeof _default === `undefined`)
|
if (typeof _default === `undefined`)
|
||||||
throw new Error(`${name} must be defined`)
|
throw new Error(`${name} must be defined`)
|
||||||
return _default
|
this.set(_default)
|
||||||
|
return this.get()
|
||||||
}
|
}
|
||||||
return v
|
try {
|
||||||
.split(/,/)
|
return caster.stringToType(v, config)
|
||||||
.map((s) => s.trim())
|
} catch (e) {
|
||||||
.filter((v) => !!v)
|
throw new Error(`${name}: ${e}`)
|
||||||
})()
|
}
|
||||||
},
|
},
|
||||||
set(v) {
|
set(v: TValue) {
|
||||||
process.env[name] = v.join(',')
|
try {
|
||||||
},
|
process.env[name] = caster.typeToString(v, config)
|
||||||
|
} catch (e) {
|
||||||
|
throw new Error(`${name}: ${e}`)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
export const mkBoolean = mkMaker<boolean>({
|
||||||
|
stringToType: (v) => castToBoolean(v),
|
||||||
|
typeToString: (v) => `${v}`,
|
||||||
|
})
|
||||||
|
|
||||||
|
export const mkNumber = mkMaker<number>({
|
||||||
|
stringToType: (s) => parseInt(s, 10),
|
||||||
|
typeToString: (v) => v.toString(),
|
||||||
|
})
|
||||||
|
|
||||||
|
export const mkPath = mkMaker<string, { create: boolean; required: boolean }>({
|
||||||
|
stringToType: (v, options) => {
|
||||||
|
const { create = false, required = true } = options || {}
|
||||||
|
if (create) {
|
||||||
|
mkdirSync(v, { recursive: true })
|
||||||
|
}
|
||||||
|
if (required && !existsSync(v)) {
|
||||||
|
throw new Error(`${v} must exist.`)
|
||||||
|
}
|
||||||
|
return v
|
||||||
|
},
|
||||||
|
typeToString: (v, options) => {
|
||||||
|
const { create = false, required = true } = options || {}
|
||||||
|
if (create) {
|
||||||
|
mkdirSync(v, { recursive: true })
|
||||||
|
}
|
||||||
|
if (required && !existsSync(v)) {
|
||||||
|
throw new Error(`${v} must exist.`)
|
||||||
|
}
|
||||||
|
return v
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
export const mkString = mkMaker<string>({
|
||||||
|
typeToString: (v) => v,
|
||||||
|
stringToType: (v) => v,
|
||||||
|
})
|
||||||
|
|
||||||
|
export const mkCsvString = mkMaker<string[]>({
|
||||||
|
typeToString: (v) => v.join(','),
|
||||||
|
stringToType: (s) =>
|
||||||
|
s
|
||||||
|
.split(/,/)
|
||||||
|
.map((s) => s.trim())
|
||||||
|
.filter((v) => !!v),
|
||||||
|
})
|
||||||
|
|
||||||
type Config<T> = {
|
type Config<T> = {
|
||||||
[K in keyof T]: HandlerFactory<T[K]>
|
[K in keyof T]: HandlerFactory<T[K]>
|
||||||
@ -132,6 +112,7 @@ export const SettingsService = <T extends Object>(config: Config<T>) => {
|
|||||||
set: (value) => handler.set(value),
|
set: (value) => handler.set(value),
|
||||||
enumerable: true,
|
enumerable: true,
|
||||||
})
|
})
|
||||||
|
handler.get() // Initialize process.env
|
||||||
}
|
}
|
||||||
|
|
||||||
return lookup as T
|
return lookup as T
|
||||||
|
@ -24,9 +24,6 @@
|
|||||||
"$shared": ["src/shared"]
|
"$shared": ["src/shared"]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"ts-node": {
|
|
||||||
"esm": true
|
|
||||||
},
|
|
||||||
"include": ["./src"],
|
"include": ["./src"],
|
||||||
"exclude": ["src/mothership-app"]
|
"exclude": ["src/mothership-app"]
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user