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
@ -7,4 +7,5 @@ attic
|
||||
build
|
||||
*.njk
|
||||
_site
|
||||
forks
|
||||
forks
|
||||
src/mothership-app/pb_hooks/src/versions.pb.js
|
@ -9,6 +9,7 @@
|
||||
"check:types:pockethost": "tsc --noEmit --skipLibCheck",
|
||||
"lint": "prettier -c \"./**/*.{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-pockethost": "concurrently 'pnpm:build:pockethost:*'",
|
||||
"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 { 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 { dbg, error, info, warn } = logger
|
||||
info(`Starting`)
|
||||
|
||||
@ -26,4 +23,6 @@ global.EventSource = require('eventsource')
|
||||
cachePath: PH_BIN_CACHE(),
|
||||
})
|
||||
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 { LoggerService, mkSingleton, SingletonBaseConfig } from '$shared'
|
||||
import { MOTHERSHIP_HOOKS_DIR, PH_BIN_CACHE } from '$constants'
|
||||
import { LoggerService, SingletonBaseConfig, mkSingleton } from '$shared'
|
||||
import { downloadAndExtract, mergeConfig, smartFetch } from '$util'
|
||||
import { keys } from '@s-libs/micro-dash'
|
||||
import Bottleneck from 'bottleneck'
|
||||
import { chmodSync, existsSync } from 'fs'
|
||||
import { chmodSync, existsSync, writeFileSync } from 'fs'
|
||||
import { join } from 'path'
|
||||
import { rsort } from 'semver'
|
||||
import { expandAndSortSemVers } from './expandAndSortSemVers'
|
||||
|
||||
type Release = {
|
||||
url: string
|
||||
@ -100,6 +101,15 @@ export const PocketbaseReleaseDownloadService = mkSingleton(
|
||||
}),
|
||||
)
|
||||
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) {
|
||||
throw new Error(
|
||||
`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 { 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
|
||||
set: (value: TValue) => void
|
||||
}
|
||||
|
||||
export type HandlerFactory<TValue> = (key: string) => Handler<TValue>
|
||||
|
||||
export type Maker<TValue, TConfig = {}> = (
|
||||
_default?: TValue,
|
||||
config?: Partial<TConfig>,
|
||||
) => HandlerFactory<TValue>
|
||||
|
||||
export const mkBoolean: Maker<boolean> = (_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 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 = {}) =>
|
||||
const mkMaker =
|
||||
<TValue, TConfig = {}>(
|
||||
caster: Caster<TValue, TConfig>,
|
||||
): Maker<TValue, TConfig> =>
|
||||
(_default, config) =>
|
||||
(name: string) => {
|
||||
const { create = false, required = true } = options
|
||||
return {
|
||||
get() {
|
||||
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 (() => {
|
||||
get(): TValue {
|
||||
const v = process.env[name]
|
||||
if (typeof v === `undefined`) {
|
||||
if (typeof _default === `undefined`)
|
||||
throw new Error(`${name} must be defined`)
|
||||
return _default
|
||||
this.set(_default)
|
||||
return this.get()
|
||||
}
|
||||
return v
|
||||
.split(/,/)
|
||||
.map((s) => s.trim())
|
||||
.filter((v) => !!v)
|
||||
})()
|
||||
},
|
||||
set(v) {
|
||||
process.env[name] = v.join(',')
|
||||
},
|
||||
try {
|
||||
return caster.stringToType(v, config)
|
||||
} catch (e) {
|
||||
throw new Error(`${name}: ${e}`)
|
||||
}
|
||||
},
|
||||
set(v: TValue) {
|
||||
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> = {
|
||||
[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),
|
||||
enumerable: true,
|
||||
})
|
||||
handler.get() // Initialize process.env
|
||||
}
|
||||
|
||||
return lookup as T
|
||||
|
@ -24,9 +24,6 @@
|
||||
"$shared": ["src/shared"]
|
||||
}
|
||||
},
|
||||
"ts-node": {
|
||||
"esm": true
|
||||
},
|
||||
"include": ["./src"],
|
||||
"exclude": ["src/mothership-app"]
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user