Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions startos/fileModels/store.json.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,8 @@ const shape = z.looseObject({
// applied, and the record of what to clear on the next change. Same
// desired-vs-actual split, and the same non-reactive read, as the two above.
officeConfigured: z.string().catch('').default(''),
// Set when a migration retires a 0.3.5 binding; cleared once reattachUiOnions succeeds.
reattachUiOnions: z.boolean().catch(false).default(false),
})

export type Store = z.infer<typeof shape>
Expand Down
2 changes: 2 additions & 0 deletions startos/init/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { setInterfaces } from '../interfaces'
import { sdk } from '../sdk'
import { versionGraph } from '../versions'
import { bootstrapNextcloud, guardUpstreamUpgrade } from './bootstrapNextcloud'
import { reattachUiOnions } from './reattachUiOnions'
import { seedFiles } from './seedFiles'

export const init = sdk.setupInit(
Expand All @@ -16,6 +17,7 @@ export const init = sdk.setupInit(
actions,
dependencies,
bootstrapNextcloud,
reattachUiOnions,
)

export const uninit = sdk.setupUninit(versionGraph)
98 changes: 98 additions & 0 deletions startos/init/reattachUiOnions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
import { ExtendedVersion, IST, T, VersionRange } from '@start9labs/start-sdk'
import { storeJson } from '../fileModels/store.json'
import { manifest } from '../manifest'
import { sdk } from '../sdk'
import { uiPort } from '../utils'

/** Tor releases whose Add Onion Service a service may run for its own hosts. */
const torAttachesForServices = VersionRange.parse('>=0.4.9.13:1')

const urlPluginMetadata = {
packageId: manifest.id,
hostId: 'main',
interfaceId: 'ui',
internalPort: uiPort,
}

/**
* Ids of the main host's .onion addresses that Tor's form offers and that no
* binding of the host already serves.
*/
async function unusedUiOnions(effects: T.Effects): Promise<string[]> {
const served = await sdk.host
.get(effects, { hostId: 'main' }, (host) =>
Object.values(host?.bindings ?? {}).flatMap((b) =>
b.addresses.available
.filter(
(a) =>
a.metadata.kind === 'plugin' && a.metadata.packageId === 'tor',
)
.map((a) => a.hostname),
),
)
.once()
const form = await effects.action.getInput({
packageId: 'tor',
actionId: 'add-onion-service',
prefill: { urlPluginMetadata },
})
const address = (form?.spec as IST.InputSpec | undefined)?.address
if (address?.type !== 'union') return []
return Object.entries(address.variants)
.filter(
([id, { name }]) =>
id.startsWith(`${manifest.id}/main/`) && !served.includes(name),
)
.map(([id]) => id)
}

/**
* Attaches the main host's unused .onion addresses to the Web UI binding after
* the retirement of 443 and 8080 left them unused, once Tor lets this service
* do it.
*/
export const reattachUiOnions = sdk.setupOnInit(async (effects) => {
const pending = await storeJson.read((s) => s.reattachUiOnions).const(effects)
if (!pending) return

const enabled = await sdk.host
.get(
effects,
{ hostId: 'main' },
(host) => host?.bindings[uiPort]?.enabled ?? false,
)
.const()
if (!enabled) return

const torVersion = await sdk
.getServiceManifest(effects, 'tor', (m) => m?.version ?? null)
.const()
if (
!torVersion ||
!ExtendedVersion.parse(torVersion).satisfies(torAttachesForServices)
)
return

try {
for (const selection of await unusedUiOnions(effects)) {
await sdk.action.run({
effects,
packageId: 'tor',
actionId: 'add-onion-service',
prefill: { urlPluginMetadata },
input: ({ spec }) => ({
urlPluginMetadata,
...('ssl' in spec ? { ssl: false } : {}),
address: { selection, value: {} },
}),
})
}
await storeJson.merge(
effects,
{ reattachUiOnions: false },
{ allowWriteAfterConst: true },
)
} catch (e) {
console.warn(`Web UI .onion addresses not reattached yet: ${String(e)}`)
}
})
28 changes: 21 additions & 7 deletions startos/versions/current.ts
Original file line number Diff line number Diff line change
@@ -1,31 +1,45 @@
import { IMPOSSIBLE, VersionInfo } from '@start9labs/start-sdk'
import { storeJson } from '../fileModels/store.json'
import { sdk } from '../sdk'

export const current = VersionInfo.of({
version: '35.0.1:1',
version: '35.0.1:2',
releaseNotes: {
en_US: `- Disable Maintenance Mode, Repair and Scan Files ask for confirmation before running.
- The Office Suite, Delete Files in Trash and External Storage settings explain what each choice does.
- Reset Admin Password offers only members of Nextcloud's admin group.
- The Delete Files in Trash choices are translated.`,
- The Delete Files in Trash choices are translated.
- Network ports left reserved by the StartOS 0.3.5 version of this package are freed. If that version had a Tor address, Nextcloud moves it to the Web UI, keeping the same .onion address, as soon as a version of Tor that allows it is installed.`,
es_ES: `- Desactivar modo de mantenimiento, Reparar y Escanear archivos piden confirmación antes de ejecutarse.
- Los ajustes Suite ofimática, Eliminar los archivos de la papelera y Almacenamiento externo explican qué hace cada opción.
- Restablecer contraseña de administrador solo ofrece miembros del grupo admin de Nextcloud.
- Las opciones de Eliminar los archivos de la papelera están traducidas.`,
- Las opciones de Eliminar los archivos de la papelera están traducidas.
- Se liberan los puertos de red que la versión de este paquete para StartOS 0.3.5 dejó reservados. Si esa versión tenía una dirección Tor, Nextcloud la traslada a la interfaz web, conservando la misma dirección .onion, en cuanto se instala una versión de Tor que lo permita.`,
de_DE: `- Wartungsmodus deaktivieren, Reparieren und Dateien scannen fragen vor der Ausführung nach einer Bestätigung.
- Die Einstellungen Office-Suite, Dateien im Papierkorb löschen und Externer Speicher erklären, was jede Auswahl bewirkt.
- Admin-Passwort zurücksetzen bietet nur Mitglieder der Nextcloud-Gruppe admin an.
- Die Auswahlmöglichkeiten von Dateien im Papierkorb löschen sind übersetzt.`,
- Die Auswahlmöglichkeiten von Dateien im Papierkorb löschen sind übersetzt.
- Netzwerkports, die die StartOS-0.3.5-Version dieses Pakets belegt gelassen hatte, werden freigegeben. Hatte diese Version eine Tor-Adresse, verlegt Nextcloud sie auf die Weboberfläche und behält dieselbe .onion-Adresse, sobald eine Tor-Version installiert ist, die das erlaubt.`,
pl_PL: `- Wyłącz tryb konserwacji, Napraw i Skanuj pliki proszą o potwierdzenie przed uruchomieniem.
- Ustawienia Pakiet biurowy, Usuwanie plików z kosza i Magazyn zewnętrzny wyjaśniają, co oznacza każdy wybór.
- Resetuj hasło administratora oferuje tylko członków grupy admin w Nextcloud.
- Opcje ustawienia Usuwanie plików z kosza są przetłumaczone.`,
- Opcje ustawienia Usuwanie plików z kosza są przetłumaczone.
- Porty sieciowe, które wersja tego pakietu dla StartOS 0.3.5 pozostawiła zajęte, zostają zwolnione. Jeśli ta wersja miała adres Tor, Nextcloud przenosi go na interfejs webowy, zachowując ten sam adres .onion, gdy tylko zostanie zainstalowana wersja Tora, która na to pozwala.`,
fr_FR: `- Désactiver le mode maintenance, Réparer et Scanner les fichiers demandent une confirmation avant de s'exécuter.
- Les réglages Suite bureautique, Suppression des fichiers de la corbeille et Stockage externe expliquent l'effet de chaque choix.
- Réinitialiser le mot de passe administrateur ne propose que les membres du groupe admin de Nextcloud.
- Les choix de Suppression des fichiers de la corbeille sont traduits.`,
- Les choix de Suppression des fichiers de la corbeille sont traduits.
- Les ports réseau que la version de ce paquet pour StartOS 0.3.5 avait laissés réservés sont libérés. Si cette version avait une adresse Tor, Nextcloud la déplace vers l'interface web, en conservant la même adresse .onion, dès qu'une version de Tor qui le permet est installée.`,
},
migrations: {
up: async () => {},
up: async ({ effects }) => {
// Tor keeps an .onion on a retired port as unused; reattachUiOnions moves it to 80.
const main = sdk.MultiHost.of(effects, 'main')
const retired = [await main.retirePort(8080), await main.retirePort(443)]
if (retired.some(Boolean)) {
await storeJson.merge(effects, { reattachUiOnions: true })
}
},
down: IMPOSSIBLE,
},
})
Loading