diff --git a/app/server/lib/FlexServer.ts b/app/server/lib/FlexServer.ts index 73c4008e..04c31971 100644 --- a/app/server/lib/FlexServer.ts +++ b/app/server/lib/FlexServer.ts @@ -1132,7 +1132,6 @@ export class FlexServer implements GristServer { await this.loadConfig(); this.addComm(); - await this.create.configure?.(); if (!isSingleUserMode()) { const externalStorage = appSettings.section('externalStorage'); const haveExternalStorage = Object.values(externalStorage.nested) @@ -1143,6 +1142,7 @@ export class FlexServer implements GristServer { this._disableExternalStorage = true; externalStorage.flag('active').set(false); } + await this.create.configure?.(); const workers = this._docWorkerMap; const docWorkerId = await this._addSelfAsWorker(workers); diff --git a/app/server/lib/ICreate.ts b/app/server/lib/ICreate.ts index b49942fd..f876fa9c 100644 --- a/app/server/lib/ICreate.ts +++ b/app/server/lib/ICreate.ts @@ -50,6 +50,7 @@ export interface ICreateActiveDocOptions { export interface ICreateStorageOptions { name: string; check(): boolean; + checkBackend?(): Promise; create(purpose: 'doc'|'meta', extraPrefix: string): ExternalStorage|undefined; } @@ -119,7 +120,10 @@ export function makeSimpleCreator(opts: { }, async configure() { for (const s of storage || []) { - if (s.check()) { break; } + if (s.check()) { + await s.checkBackend?.(); + break; + } } }, ...(opts.shell && { diff --git a/app/server/lib/MinIOExternalStorage.ts b/app/server/lib/MinIOExternalStorage.ts index dafa738a..fe70ea4b 100644 --- a/app/server/lib/MinIOExternalStorage.ts +++ b/app/server/lib/MinIOExternalStorage.ts @@ -107,6 +107,11 @@ export class MinIOExternalStorage implements ExternalStorage { } } + public async hasVersioning(): Promise { + const versioning = await this._s3.getBucketVersioning(this.bucket); + return versioning && versioning.Status === 'Enabled'; + } + public async versions(key: string, options?: { includeDeleteMarkers?: boolean }) { const results: minio.BucketItem[] = []; await new Promise((resolve, reject) => { diff --git a/app/server/lib/configureMinIOExternalStorage.ts b/app/server/lib/configureMinIOExternalStorage.ts index c34a4755..2cac4d2c 100644 --- a/app/server/lib/configureMinIOExternalStorage.ts +++ b/app/server/lib/configureMinIOExternalStorage.ts @@ -60,3 +60,16 @@ export function checkMinIOExternalStorage() { region }; } + +export async function checkMinIOBucket() { + const options = checkMinIOExternalStorage(); + if (!options) { + throw new Error('Configuration check failed for MinIO backend storage.'); + } + + const externalStorage = new MinIOExternalStorage(options.bucket, options); + if (!await externalStorage.hasVersioning()) { + await externalStorage.close(); + throw new Error(`FATAL: the MinIO bucket "${options.bucket}" does not have versioning enabled`); + } +} diff --git a/app/server/mergedServerMain.ts b/app/server/mergedServerMain.ts index cee69713..6b640542 100644 --- a/app/server/mergedServerMain.ts +++ b/app/server/mergedServerMain.ts @@ -106,45 +106,50 @@ export async function main(port: number, serverTypes: ServerType[], server.addApiMiddleware(); await server.addBillingMiddleware(); - await server.start(); + try { + await server.start(); - if (includeHome) { - server.addUsage(); - if (!includeDocs) { - server.addDocApiForwarder(); + if (includeHome) { + server.addUsage(); + if (!includeDocs) { + server.addDocApiForwarder(); + } + server.addJsonSupport(); + await server.addLandingPages(); + // todo: add support for home api to standalone app + server.addHomeApi(); + server.addBillingApi(); + server.addNotifier(); + await server.addTelemetry(); + await server.addHousekeeper(); + await server.addLoginRoutes(); + server.addAccountPage(); + server.addBillingPages(); + server.addWelcomePaths(); + server.addLogEndpoint(); + server.addGoogleAuthEndpoint(); + server.addInstallEndpoints(); } - server.addJsonSupport(); - await server.addLandingPages(); - // todo: add support for home api to standalone app - server.addHomeApi(); - server.addBillingApi(); - server.addNotifier(); - await server.addTelemetry(); - await server.addHousekeeper(); - await server.addLoginRoutes(); - server.addAccountPage(); - server.addBillingPages(); - server.addWelcomePaths(); - server.addLogEndpoint(); - server.addGoogleAuthEndpoint(); - server.addInstallEndpoints(); + + if (includeDocs) { + server.addJsonSupport(); + await server.addTelemetry(); + await server.addDoc(); + } + + if (includeHome) { + server.addClientSecrets(); + } + + server.finalize(); + + server.checkOptionCombinations(); + server.summary(); + return server; + } catch(e) { + await server.close(); + throw e; } - - if (includeDocs) { - server.addJsonSupport(); - await server.addTelemetry(); - await server.addDoc(); - } - - if (includeHome) { - server.addClientSecrets(); - } - - server.finalize(); - - server.checkOptionCombinations(); - server.summary(); - return server; } diff --git a/package.json b/package.json index f0c5f83b..c104212d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "grist-core", - "version": "1.1.1", + "version": "1.1.2", "license": "Apache-2.0", "description": "Grist is the evolution of spreadsheets", "homepage": "https://github.com/gristlabs/grist-core", @@ -13,8 +13,8 @@ "install:python3": "buildtools/prepare_python3.sh", "build:prod": "buildtools/build.sh", "start:prod": "sandbox/run.sh", - "test": "GRIST_SESSION_COOKIE=grist_test_cookie GRIST_TEST_LOGIN=1 TEST_SUPPORT_API_KEY=api_key_for_support TEST_CLEAN_DATABASE=true mocha ${DEBUG:+-b --no-exit} --slow 8000 ${DEBUG:---forbid-only} -g \"${GREP_TESTS}\" '_build/test/common/*.js' '_build/test/client/*.js' '_build/test/nbrowser/*.js' '_build/test/server/**/*.js' '_build/test/gen-server/**/*.js'", - "test:nbrowser": "TEST_SUITE=nbrowser TEST_SUITE_FOR_TIMINGS=nbrowser TIMINGS_FILE=test/timings/nbrowser.txt GRIST_SESSION_COOKIE=grist_test_cookie GRIST_TEST_LOGIN=1 TEST_SUPPORT_API_KEY=api_key_for_support TEST_CLEAN_DATABASE=true mocha ${DEBUG:+-b --no-exit} ${DEBUG:---forbid-only} -g \"${GREP_TESTS}\" --slow 8000 -R test/xunit-file '_build/test/nbrowser/**/*.js'", + "test": "GRIST_SESSION_COOKIE=grist_test_cookie GRIST_TEST_LOGIN=1 TEST_SUPPORT_API_KEY=api_key_for_support TEST_CLEAN_DATABASE=true mocha ${DEBUG:+-b --no-exit} --slow 8000 $([ -z $DEBUG ] && echo --forbid-only) -g \"${GREP_TESTS}\" '_build/test/common/*.js' '_build/test/client/*.js' '_build/test/nbrowser/*.js' '_build/test/server/**/*.js' '_build/test/gen-server/**/*.js'", + "test:nbrowser": "TEST_SUITE=nbrowser TEST_SUITE_FOR_TIMINGS=nbrowser TIMINGS_FILE=test/timings/nbrowser.txt GRIST_SESSION_COOKIE=grist_test_cookie GRIST_TEST_LOGIN=1 TEST_SUPPORT_API_KEY=api_key_for_support TEST_CLEAN_DATABASE=true mocha ${DEBUG:+-b --no-exit} $([ -z $DEBUG ] && echo --forbid-only) -g \"${GREP_TESTS}\" --slow 8000 -R test/xunit-file '_build/test/nbrowser/**/*.js'", "test:client": "GRIST_SESSION_COOKIE=grist_test_cookie mocha ${DEBUG:+'-b'} '_build/test/client/**/*.js'", "test:common": "GRIST_SESSION_COOKIE=grist_test_cookie mocha ${DEBUG:+'-b'} '_build/test/common/**/*.js'", "test:server": "TEST_SUITE=server TEST_SUITE_FOR_TIMINGS=server TIMINGS_FILE=test/timings/server.txt GRIST_SESSION_COOKIE=grist_test_cookie mocha ${DEBUG:+'-b'} -R test/xunit-file '_build/test/server/**/*.js' '_build/test/gen-server/**/*.js'", @@ -190,11 +190,13 @@ "@gristlabs/sqlite3": "5.1.4-grist.8" }, "mocha": { - "require": ["test/setupPaths", - "source-map-support/register", - "test/report-why-tests-hang", - "test/init-mocha-webdriver", - "test/split-tests", - "test/chai-as-promised"] + "require": [ + "test/setupPaths", + "source-map-support/register", + "test/report-why-tests-hang", + "test/init-mocha-webdriver", + "test/split-tests", + "test/chai-as-promised" + ] } } diff --git a/stubs/app/server/lib/create.ts b/stubs/app/server/lib/create.ts index 98c11b52..370d0c5a 100644 --- a/stubs/app/server/lib/create.ts +++ b/stubs/app/server/lib/create.ts @@ -1,4 +1,4 @@ -import { checkMinIOExternalStorage, +import { checkMinIOBucket, checkMinIOExternalStorage, configureMinIOExternalStorage } from 'app/server/lib/configureMinIOExternalStorage'; import { makeSimpleCreator } from 'app/server/lib/ICreate'; import { Telemetry } from 'app/server/lib/Telemetry'; @@ -12,6 +12,7 @@ export const create = makeSimpleCreator({ { name: 'minio', check: () => checkMinIOExternalStorage() !== undefined, + checkBackend: () => checkMinIOBucket(), create: configureMinIOExternalStorage, }, ], diff --git a/stubs/app/server/server.ts b/stubs/app/server/server.ts index dc462275..9ea30b40 100644 --- a/stubs/app/server/server.ts +++ b/stubs/app/server/server.ts @@ -117,5 +117,7 @@ export async function main() { } if (require.main === module) { - main().catch((err) => console.error(err)); + main().catch((err) => { + console.error(err); + }); }