diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 992ba865..ac31b00c 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -4,5 +4,5 @@ You are eager to contribute to Grist? That's awesome! See below some contributio - [translate](/documentation/translations.md) - [write tutorials and user documentation](https://github.com/gristlabs/grist-help?tab=readme-ov-file#grist-help-center) - [develop](/documentation/develop.md) -- [report issues or suggest enhancement](https://github.com/gristlabs/grist-core/issues/new) +- [report issues or suggest enhancement](https://github.com/gristlabs/grist-core/issues/new/choose) diff --git a/README.md b/README.md index 401baf37..d2e0a9e4 100644 --- a/README.md +++ b/README.md @@ -312,6 +312,8 @@ Grist can be configured in many ways. Here are the main environment variables it | GRIST_UI_FEATURES | comma-separated list of UI features to enable. Allowed names of parts: `helpCenter,billing,templates,createSite,multiSite,multiAccounts,sendToDrive,tutorials,supportGrist`. If a part also exists in GRIST_HIDE_UI_ELEMENTS, it won't be enabled. | | GRIST_UNTRUSTED_PORT | if set, plugins will be served from the given port. This is an alternative to setting APP_UNTRUSTED_URL. | | GRIST_WIDGET_LIST_URL | a url pointing to a widget manifest, by default `https://github.com/gristlabs/grist-widget/releases/download/latest/manifest.json` is used | +| GRIST_LOG_HTTP | When set to `true`, log HTTP requests and responses information. Defaults to `false`. | +| GRIST_LOG_HTTP_BODY | When this variable and `GRIST_LOG_HTTP` are set to `true` , log the body along with the HTTP requests. :warning: Be aware it may leak confidential information in the logs.:warning: Defaults to `false`. | | COOKIE_MAX_AGE | session cookie max age, defaults to 90 days; can be set to "none" to make it a session cookie | | HOME_PORT | port number to listen on for REST API server; if set to "share", add API endpoints to regular grist port. | | PORT | port number to listen on for Grist server | diff --git a/app/server/lib/FlexServer.ts b/app/server/lib/FlexServer.ts index 37070d98..5391b5f2 100644 --- a/app/server/lib/FlexServer.ts +++ b/app/server/lib/FlexServer.ts @@ -442,24 +442,33 @@ export class FlexServer implements GristServer { public addLogging() { if (this._check('logging')) { return; } - if (process.env.GRIST_LOG_SKIP_HTTP) { return; } + if (!this._httpLoggingEnabled()) { return; } // Add a timestamp token that matches exactly the formatting of non-morgan logs. morganLogger.token('logTime', (req: Request) => log.timestamp()); // Add an optional gristInfo token that can replace the url, if the url is sensitive. morganLogger.token('gristInfo', (req: RequestWithGristInfo) => req.gristInfo || req.originalUrl || req.url); morganLogger.token('host', (req: express.Request) => req.get('host')); - const msg = ':logTime :host :method :gristInfo :status :response-time ms - :res[content-length]'; + morganLogger.token('body', (req: express.Request) => + req.is('application/json') ? JSON.stringify(req.body) : undefined + ); + + // For debugging, be careful not to enable logging in production (may log sensitive data) + const shouldLogBody = isAffirmative(process.env.GRIST_LOG_HTTP_BODY); + + const msg = `:logTime :host :method :gristInfo ${shouldLogBody ? ':body ' : ''}` + + ":status :response-time ms - :res[content-length]"; // In hosted Grist, render json so logs retain more organization. function outputJson(tokens: any, req: any, res: any) { return JSON.stringify({ timestamp: tokens.logTime(req, res), + host: tokens.host(req, res), method: tokens.method(req, res), path: tokens.gristInfo(req, res), + ...(shouldLogBody ? { body: tokens.body(req, res) } : {}), status: tokens.status(req, res), timeMs: parseFloat(tokens['response-time'](req, res)) || undefined, contentLength: parseInt(tokens.res(req, res, 'content-length'), 10) || undefined, - host: tokens.host(req, res), altSessionId: req.altSessionId, }); } @@ -2489,6 +2498,33 @@ export class FlexServer implements GristServer { []; return [...pluggedMiddleware, sessionClearMiddleware]; } + + /** + * Returns true if GRIST_LOG_HTTP="true" (or any truthy value). + * Returns true if GRIST_LOG_SKIP_HTTP="" (empty string). + * Returns false otherwise. + * + * Also displays a deprecation warning if GRIST_LOG_SKIP_HTTP is set to any value ("", "true", whatever...), + * and throws an exception if GRIST_LOG_SKIP_HTTP and GRIST_LOG_HTTP are both set to make the server crash. + */ + private _httpLoggingEnabled(): boolean { + const deprecatedOptionEnablesLog = process.env.GRIST_LOG_SKIP_HTTP === ''; + const isGristLogHttpEnabled = isAffirmative(process.env.GRIST_LOG_HTTP); + + if (process.env.GRIST_LOG_HTTP !== undefined && process.env.GRIST_LOG_SKIP_HTTP !== undefined) { + throw new Error('Both GRIST_LOG_HTTP and GRIST_LOG_SKIP_HTTP are set. ' + + 'Please remove GRIST_LOG_SKIP_HTTP and set GRIST_LOG_HTTP to the value you actually want.'); + } + + if (process.env.GRIST_LOG_SKIP_HTTP !== undefined) { + const expectedGristLogHttpVal = deprecatedOptionEnablesLog ? "true" : "false"; + + log.warn(`Setting env variable GRIST_LOG_SKIP_HTTP="${process.env.GRIST_LOG_SKIP_HTTP}" ` + + `is deprecated in favor of GRIST_LOG_HTTP="${expectedGristLogHttpVal}"`); + } + + return isGristLogHttpEnabled || deprecatedOptionEnablesLog; + } } /** diff --git a/buildtools/.grist-ee-version b/buildtools/.grist-ee-version index c81aa44a..e3e18070 100644 --- a/buildtools/.grist-ee-version +++ b/buildtools/.grist-ee-version @@ -1 +1 @@ -0.9.7 +0.9.8 diff --git a/docker-compose-examples/grist-local-testing/README.md b/docker-compose-examples/grist-local-testing/README.md index cd610cfd..ad45379b 100644 --- a/docker-compose-examples/grist-local-testing/README.md +++ b/docker-compose-examples/grist-local-testing/README.md @@ -9,4 +9,11 @@ See https://support.getgrist.com/self-managed for more information. ## How to run this example -This example can be run with `docker compose up`. \ No newline at end of file +To run this example, change to the directory containing this example, and run: +```sh +docker compose up +``` +Then you should be able to visit your local Grist instance at . + +This will start an instance that stores its documents and files in the `persist/` subdirectory. +You can change this location using the `PERSIST_DIR` environment variable. diff --git a/docker-compose-examples/grist-local-testing/docker-compose.yml b/docker-compose-examples/grist-local-testing/docker-compose.yml index 028d4e0f..5ccf7399 100644 --- a/docker-compose-examples/grist-local-testing/docker-compose.yml +++ b/docker-compose-examples/grist-local-testing/docker-compose.yml @@ -3,6 +3,6 @@ services: image: gristlabs/grist:latest volumes: # Where to store persistent data, such as documents. - - ${PERSIST_DIR}/grist:/persist + - ${PERSIST_DIR:-./persist}/grist:/persist ports: - 8484:8484 diff --git a/docker-compose-examples/grist-local-testing/persist/.gitkeep b/docker-compose-examples/grist-local-testing/persist/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/static/locales/ca.client.json b/static/locales/ca.client.json index 0967ef42..62b52dfa 100644 --- a/static/locales/ca.client.json +++ b/static/locales/ca.client.json @@ -1 +1,19 @@ -{} +{ + "ACUserManager": { + "Invite new member": "Convidar nou membre", + "Enter email address": "Posa l'adreça de correu electrònic", + "We'll email an invite to {{email}}": "Enviarem una invitació per correu electrònic a {{email}}" + }, + "AccessRules": { + "Add Default Rule": "Afegir regla per defecte", + "Add Table Rules": "Afegir regles de Taula", + "Add User Attributes": "Afegir atributs d'usuari", + "Allow everyone to view Access Rules.": "Permetre a tothom veure les Regles d'Accés.", + "Attribute name": "Nom de l'Atribut", + "Attribute to Look Up": "Atribut a buscar", + "Checking...": "Comprovant…", + "Condition": "Condició", + "Add Column Rule": "Afegir regla de columna", + "Allow everyone to copy the entire document, or view it in full in fiddle mode.\nUseful for examples and templates, but not for sensitive data.": "Permetre a tothom copiar el document complet, o veure'l en mode completament obert.\nÚtil per a exemples i plantilles, però no per a dades sensibles." + } +} diff --git a/static/locales/eu.client.json b/static/locales/eu.client.json index ecb6a0e0..d332690b 100644 --- a/static/locales/eu.client.json +++ b/static/locales/eu.client.json @@ -667,7 +667,7 @@ "Replace {{termToUse}}...": "Ordeztu {{termToUse}}…", "Save Document": "Gorde dokumentua", "Send to Google Drive": "Bidali Google Drivera", - "Show in folder": "Erakutsi direktorioan", + "Show in folder": "Erakutsi karpetan", "Work on a Copy": "Egin lan kopia batean", "Share": "Partekatu", "Download...": "Jaitsi…", diff --git a/static/locales/fr.client.json b/static/locales/fr.client.json index 4d70bc16..d83c9e9a 100644 --- a/static/locales/fr.client.json +++ b/static/locales/fr.client.json @@ -321,7 +321,7 @@ "Manage webhooks": "Gérer les points d'ancrage Web", "ID for API use": "ID pour l'utilisation de l'API", "Find slow formulas": "Trouver les formules lentes", - "python2 (legacy)": "python2 (encien)", + "python2 (legacy)": "python2 (ancien)", "Try API calls from the browser": "Essayer les appels API à partir du navigateur", "Time Zone": "Fuseau horaire", "python3 (recommended)": "python3 (recommandé)", diff --git a/static/locales/sk.client.json b/static/locales/sk.client.json index e3a9f146..91301f18 100644 --- a/static/locales/sk.client.json +++ b/static/locales/sk.client.json @@ -1309,7 +1309,9 @@ "unknown": "neznáme", "Grist signs user session cookies with a secret key. Please set this key via the environment variable GRIST_SESSION_SECRET. Grist falls back to a hard-coded default when it is not set. We may remove this notice in the future as session IDs generated since v1.1.16 are inherently cryptographically secure.": "Grist podpisuje súbory cookie relácie používateľa tajným kľúčom. Nastavte tento kľúč prostredníctvom premennej prostredia GRIST_SESSION_SECRET. Grist sa vráti späť na pevne zakódované predvolené nastavenie, keď nie je nastavené. Toto upozornenie môžeme v budúcnosti odstrániť, pretože ID relácie generované od verzie 1.1.16 sú vo svojej podstate kryptograficky bezpečné.", "Key to sign sessions with": "Kľúč na podpisovanie relácií", - "Session Secret": "Tajomstvo Relácie" + "Session Secret": "Tajomstvo Relácie", + "Enable Grist Enterprise": "Povoliť Grist Enterprise", + "Enterprise": "Enterprise" }, "TimingPage": { "Table ID": "ID Tabuľky", @@ -1666,5 +1668,18 @@ "Type here": "Zadať sem", "Your organization": "Vaša organizácia", "Your role": "Vaša úloha" + }, + "ToggleEnterpriseWidget": { + "Disable Grist Enterprise": "Zakázať Grist Enterprise", + "Enable Grist Enterprise": "Povoliť Grist Enterprise", + "Grist Enterprise is **enabled**.": "Grist Enterprise je **povolený**.", + "An activation key is used to run Grist Enterprise after a trial period\nof 30 days has expired. Get an activation key by [signing up for Grist\nEnterprise]({{signupLink}}). You do not need an activation key to run\nGrist Core.\n\nLearn more in our [Help Center]({{helpCenter}}).": "Aktivačný kľúč sa používa na spustenie Grist Enterprise po uplynutí skúšobnej\ndoby 30 dní. Získajte aktivačný kľúč [registráciou pre Grist\nPodnik]({{signupLink}}). Na spustenie nepotrebujete aktivačný kľúč\nGrist Core.\n\nZistite viac v našom [Help Center] ({{helpCenter}})." + }, + "ViewLayout": { + "Delete": "Odstrániť", + "Delete data and this widget.": "Odstrániť dáta a tento widget.", + "Keep data and delete widget. Table will remain available in {{rawDataLink}}": "Uchovať údaje a odstrániť widget. Tabuľka zostane dostupná v {{rawDataLink}}", + "raw data page": "stránka nespracovaných údajov", + "Table {{tableName}} will no longer be visible": "Tabuľka {{tableName}} nebude dlho viditeľná" } } diff --git a/stubs/app/server/server.ts b/stubs/app/server/server.ts index 6fbffdf5..c7ccc7be 100644 --- a/stubs/app/server/server.ts +++ b/stubs/app/server/server.ts @@ -15,7 +15,6 @@ const debugging = isAffirmative(process.env.DEBUG) || isAffirmative(process.env. if (!debugging) { // Be a lot less noisy by default. setDefaultEnv('GRIST_LOG_LEVEL', 'error'); - setDefaultEnv('GRIST_LOG_SKIP_HTTP', 'true'); } // Use a distinct cookie. Bump version to 2. diff --git a/test/test_under_docker.sh b/test/test_under_docker.sh index fd75fb10..6663691d 100755 --- a/test/test_under_docker.sh +++ b/test/test_under_docker.sh @@ -31,6 +31,8 @@ cleanup() { GRIST_LOG_LEVEL="error" if [[ "${DEBUG:-}" == 1 ]]; then GRIST_LOG_LEVEL="" + GRIST_LOG_HTTP="true" + GRIST_LOG_HTTP_BODY="true" fi docker run --name $DOCKER_CONTAINER --rm \ @@ -39,7 +41,8 @@ docker run --name $DOCKER_CONTAINER --rm \ --env GRIST_SESSION_COOKIE=grist_test_cookie \ --env GRIST_TEST_LOGIN=1 \ --env GRIST_LOG_LEVEL=$GRIST_LOG_LEVEL \ - --env GRIST_LOG_SKIP_HTTP=${DEBUG:-false} \ + --env GRIST_LOG_HTTP=${GRIST_LOG_HTTP:-false} \ + --env GRIST_LOG_HTTP_BODY=${GRIST_LOG_HTTP_BODY:-false} \ --env TEST_SUPPORT_API_KEY=api_key_for_support \ --env GRIST_TEMPLATE_ORG=templates \ ${TEST_IMAGE:-gristlabs/grist} &