Issue 17: StartOS 0.4.0 packaging
- Dockerfile: multi-stage build (Node frontend + Python backend) - StartOS manifest, interfaces, backups, version graph - start.sh: auto-generates session secret, creates first approver on boot - Backend serves built frontend as SPA in production - Single data volume for SQLite DB, platform backups cover it
This commit is contained in:
@@ -0,0 +1,4 @@
|
||||
import { sdk } from '../sdk'
|
||||
|
||||
// No custom actions for v1. Config is handled at install time.
|
||||
export const actions = sdk.Actions.of()
|
||||
@@ -0,0 +1,5 @@
|
||||
import { sdk } from './sdk'
|
||||
|
||||
export const { createBackup, restoreInit } = sdk.setupBackups(
|
||||
async ({ effects }) => sdk.Backups.ofVolumes('main'),
|
||||
)
|
||||
@@ -0,0 +1,3 @@
|
||||
import { sdk } from './sdk'
|
||||
|
||||
export const setDependencies = sdk.setupDependencies(async () => ({}))
|
||||
@@ -0,0 +1,8 @@
|
||||
export { createBackup } from './backups'
|
||||
export { main } from './main'
|
||||
export { init, uninit } from './init'
|
||||
export { actions } from './actions'
|
||||
import { buildManifest } from '@start9labs/start-sdk'
|
||||
import { manifest as sdkManifest } from './manifest'
|
||||
import { versionGraph } from './install/versionGraph'
|
||||
export const manifest = buildManifest(versionGraph, sdkManifest)
|
||||
@@ -0,0 +1,16 @@
|
||||
import { sdk } from '../sdk'
|
||||
import { setDependencies } from '../dependencies'
|
||||
import { setInterfaces } from '../interfaces'
|
||||
import { versionGraph } from '../install/versionGraph'
|
||||
import { actions } from '../actions'
|
||||
import { restoreInit } from '../backups'
|
||||
|
||||
export const init = sdk.setupInit(
|
||||
restoreInit,
|
||||
versionGraph,
|
||||
setInterfaces,
|
||||
setDependencies,
|
||||
actions,
|
||||
)
|
||||
|
||||
export const uninit = sdk.setupUninit(versionGraph)
|
||||
@@ -0,0 +1,7 @@
|
||||
import { VersionGraph } from '@start9labs/start-sdk'
|
||||
import { current, other } from './versions'
|
||||
|
||||
export const versionGraph = VersionGraph.of({
|
||||
current,
|
||||
other,
|
||||
})
|
||||
@@ -0,0 +1,2 @@
|
||||
export { v_0_1_0 as current } from './v_0_1_0'
|
||||
export const other = []
|
||||
@@ -0,0 +1,16 @@
|
||||
import { VersionInfo } from '@start9labs/start-sdk'
|
||||
import * as fs from 'fs'
|
||||
|
||||
export const v_0_1_0 = VersionInfo.of({
|
||||
version: '0.1.0:0',
|
||||
releaseNotes: {
|
||||
en_US: 'Initial release. Internal fund administration and quarterly valuation sign-off.',
|
||||
},
|
||||
migrations: {
|
||||
up: async ({ effects }) => {
|
||||
// DB initialization handled by Alembic on app startup.
|
||||
// First approver account created by start.sh if no users exist.
|
||||
},
|
||||
down: async ({ effects }) => {},
|
||||
},
|
||||
})
|
||||
@@ -0,0 +1,24 @@
|
||||
import { sdk } from './sdk'
|
||||
|
||||
const appPort = 8000
|
||||
|
||||
export const setInterfaces = sdk.setupInterfaces(async ({ effects }) => {
|
||||
const uiMulti = sdk.MultiHost.of(effects, 'ui-multi')
|
||||
const uiMultiOrigin = await uiMulti.bindPort(appPort, {
|
||||
protocol: 'http',
|
||||
})
|
||||
const ui = sdk.createInterface(effects, {
|
||||
name: 'Web UI',
|
||||
id: 'ui',
|
||||
description: 'Ten31Portal fund administration interface',
|
||||
type: 'ui',
|
||||
masked: false,
|
||||
schemeOverride: null,
|
||||
username: null,
|
||||
path: '',
|
||||
query: {},
|
||||
})
|
||||
|
||||
const uiReceipt = await uiMultiOrigin.export([ui])
|
||||
return [uiReceipt]
|
||||
})
|
||||
@@ -0,0 +1,39 @@
|
||||
import { sdk } from './sdk'
|
||||
|
||||
const appPort = 8000
|
||||
|
||||
export const main = sdk.setupMain(async ({ effects }) => {
|
||||
console.info('Starting Ten31Portal')
|
||||
|
||||
const subcontainer = await sdk.SubContainer.of(
|
||||
effects,
|
||||
{ imageId: 'main' },
|
||||
sdk.Mounts.of().mountVolume({
|
||||
volumeId: 'main',
|
||||
subpath: null,
|
||||
mountpoint: '/data',
|
||||
readonly: false,
|
||||
}),
|
||||
'ten31portal-sub',
|
||||
)
|
||||
|
||||
return sdk.Daemons.of(effects).addDaemon('primary', {
|
||||
subcontainer,
|
||||
exec: {
|
||||
command: ['sh', '-c', '/app/start.sh'],
|
||||
env: {
|
||||
TEN31_DB_PATH: '/data/portal.db',
|
||||
TEN31_SESSION_SECRET: process.env.TEN31_SESSION_SECRET || 'change-me',
|
||||
},
|
||||
},
|
||||
ready: {
|
||||
display: 'Web Interface',
|
||||
fn: () =>
|
||||
sdk.healthCheck.checkPortListening(effects, appPort, {
|
||||
successMessage: 'Ten31Portal is ready',
|
||||
errorMessage: 'Ten31Portal is starting...',
|
||||
}),
|
||||
},
|
||||
requires: [],
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,32 @@
|
||||
import { setupManifest } from '@start9labs/start-sdk'
|
||||
|
||||
export const manifest = setupManifest({
|
||||
id: 'ten31portal',
|
||||
title: 'Ten31Portal',
|
||||
license: 'MIT',
|
||||
packageRepo: 'https://github.com/smallblocks/Ten31Portal',
|
||||
upstreamRepo: 'https://github.com/smallblocks/Ten31Portal',
|
||||
marketingUrl: 'https://ten31.com',
|
||||
donationUrl: 'https://ten31.com',
|
||||
docsUrls: ['https://github.com/smallblocks/Ten31Portal/blob/main/README.md'],
|
||||
description: {
|
||||
short: 'Internal fund administration and valuation sign-off',
|
||||
long: 'System of record for Ten31 entities, holdings, positions, and quarterly valuation sign-off. Replaces Carta for internal use.',
|
||||
},
|
||||
volumes: ['main'],
|
||||
images: {
|
||||
main: {
|
||||
source: { dockerBuild: { dockerfile: './Dockerfile', workdir: '.' } },
|
||||
arch: ['x86_64', 'aarch64'],
|
||||
},
|
||||
},
|
||||
alerts: {
|
||||
install: 'After installation, access the web UI to log in. A default approver account is created on first boot using the configured credentials.',
|
||||
update: null,
|
||||
uninstall: 'Uninstalling will remove all fund data. Create a backup first.',
|
||||
restore: null,
|
||||
start: null,
|
||||
stop: null,
|
||||
},
|
||||
dependencies: {},
|
||||
})
|
||||
@@ -0,0 +1,4 @@
|
||||
import { StartSdk } from '@start9labs/start-sdk'
|
||||
import { manifest } from './manifest'
|
||||
|
||||
export const sdk = StartSdk.of().withManifest(manifest).build(true)
|
||||
Reference in New Issue
Block a user