From 2c5a03b3ee3cc546659a931e25e16ab028a7439f Mon Sep 17 00:00:00 2001 From: Tilman Vatteroth Date: Sun, 2 May 2021 22:38:43 +0200 Subject: [PATCH] Restructures + New Evironment Variables (#1230) * Use document base uri for react router Signed-off-by: Tilman Vatteroth * Rename getAndSetUser to fetchAndSetUser Getter should be reserved for simple get functions. Everything that does a bit more logic should use a more meaningful verb. Signed-off-by: Tilman Vatteroth * Rename getFrontPageContent to fetchFrontPageContent Getter should be reserved for simple get functions. Everything that does a bit more logic should use a more meaningful verb. Signed-off-by: Tilman Vatteroth * Reformat code Signed-off-by: Tilman Vatteroth * Use PUBLIC_URL env var in index.html Signed-off-by: Tilman Vatteroth * Introduce new environment variables For better testing (especially if you have multiple endpoints) this commit introduces REACT_APP_BACKEND_BASE_URL, REACT_APP_FRONTEND_ASSETS_URL and REACT_APP_CUSTOMIZE_ASSETS_URL Signed-off-by: Tilman Vatteroth * Remove redundant license information Signed-off-by: Tilman Vatteroth * Remove redundant license information Signed-off-by: Tilman Vatteroth * Fix rebase issues Signed-off-by: Tilman Vatteroth * Remove unused file Signed-off-by: Tilman Vatteroth * Correct parameter Signed-off-by: Tilman Vatteroth * Fix run tasks Signed-off-by: Tilman Vatteroth * Force use of bash Signed-off-by: Tilman Vatteroth * Fix link to cypress picture Signed-off-by: Tilman Vatteroth * revert change Signed-off-by: Tilman Vatteroth * fix url Signed-off-by: Tilman Vatteroth * Remove license info Signed-off-by: Tilman Vatteroth * Revert rebase issues Signed-off-by: Tilman Vatteroth * Add missing banner code Signed-off-by: Tilman Vatteroth * Fix test url Signed-off-by: Tilman Vatteroth * Useless change to trigger github Signed-off-by: Tilman Vatteroth * Don't set backend base url because this break the mock mode detection Signed-off-by: Tilman Vatteroth Co-authored-by: Philip Molares --- .github/workflows/build.yml | 2 +- .reuse/dep5 | 16 ++-- README.md | 1 - cypress/integration/fileUpload.spec.ts | 4 +- cypress/integration/history.spec.ts | 4 +- cypress/integration/intro.spec.ts | 2 +- cypress/integration/profile.spec.ts | 6 +- cypress/integration/signInButton.spec.ts | 2 +- cypress/support/config.ts | 4 +- cypress/support/visit-test-editor.ts | 2 +- package.json | 9 +- public/api/private/notes/features-get | 18 ---- public/img/demo.png.license | 3 - public/index.html | 15 ++-- public/{ => mock-backend}/api/private/config | 6 +- public/{ => mock-backend}/api/private/me-get | 2 +- .../{ => mock-backend}/api/private/me/history | 0 .../api/private/notes/banner-get | 0 .../api/private/notes/features-get | 18 ++++ .../api/private/notes/features/revisions-list | 0 .../notes/features/revisions/1598389571 | 0 .../notes/features/revisions/1598390307 | 0 .../api/private/notes/old-get | 0 public/{ => mock-backend}/api/private/tokens | 0 .../api/private/users/dermolly | 2 +- .../api/private/users/emcrx | 2 +- .../api/private/users/mrdrogdrog | 2 +- .../{ => mock-backend/public}/img/avatar.png | Bin public/{ => mock-backend/public}/img/demo.png | Bin .../{ => mock-backend/public}/img/highres.jpg | Bin public/{ => mock-backend/public}/intro.md | 2 +- .../{ => mock-backend/public}/screenshot.png | Bin public/screenshot.png.license | 3 - .../application-loader/application-loader.tsx | 19 ++-- ...nfigLoader.ts => fetch-frontend-config.ts} | 10 +-- .../application-loader/initializers/i18n.ts | 4 +- .../application-loader/initializers/index.ts | 17 +++- .../hooks/use-intro-page-content.ts | 10 +-- src/components/intro-page/requests.ts | 4 +- .../footer/powered-by-links.tsx | 2 +- src/components/login-page/auth/utils.ts | 2 +- .../login-page/auth/via-internal.tsx | 4 +- src/components/login-page/auth/via-ldap.tsx | 10 +-- src/components/login-page/auth/via-openid.tsx | 10 +-- .../graphviz/graphviz-frame.tsx | 7 +- .../settings/profile-display-name.tsx | 10 +-- .../register-page/register-page.tsx | 82 +++++++++--------- src/hooks/common/use-backend-base-url.ts | 9 ++ src/hooks/common/use-customize-assets-url.ts | 12 +++ src/hooks/common/use-frontend-assets-url.ts | 12 +++ src/index.tsx | 4 +- src/utils/test-modes.ts | 2 +- 52 files changed, 193 insertions(+), 162 deletions(-) delete mode 100644 public/api/private/notes/features-get delete mode 100644 public/img/demo.png.license rename public/{ => mock-backend}/api/private/config (87%) rename public/{ => mock-backend}/api/private/me-get (64%) rename public/{ => mock-backend}/api/private/me/history (100%) rename public/{ => mock-backend}/api/private/notes/banner-get (100%) create mode 100644 public/mock-backend/api/private/notes/features-get rename public/{ => mock-backend}/api/private/notes/features/revisions-list (100%) rename public/{ => mock-backend}/api/private/notes/features/revisions/1598389571 (100%) rename public/{ => mock-backend}/api/private/notes/features/revisions/1598390307 (100%) rename public/{ => mock-backend}/api/private/notes/old-get (100%) rename public/{ => mock-backend}/api/private/tokens (100%) rename public/{ => mock-backend}/api/private/users/dermolly (63%) rename public/{ => mock-backend}/api/private/users/emcrx (62%) rename public/{ => mock-backend}/api/private/users/mrdrogdrog (64%) rename public/{ => mock-backend/public}/img/avatar.png (100%) rename public/{ => mock-backend/public}/img/demo.png (100%) rename public/{ => mock-backend/public}/img/highres.jpg (100%) rename public/{ => mock-backend/public}/intro.md (67%) rename public/{ => mock-backend/public}/screenshot.png (100%) delete mode 100644 public/screenshot.png.license rename src/components/application-loader/initializers/{configLoader.ts => fetch-frontend-config.ts} (68%) create mode 100644 src/hooks/common/use-backend-base-url.ts create mode 100644 src/hooks/common/use-customize-assets-url.ts create mode 100644 src/hooks/common/use-frontend-assets-url.ts diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index b5eae2969..6f4f665c5 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -40,4 +40,4 @@ jobs: - name: Test Project run: yarn test - name: Build project - run: yarn build:production + run: yarn build:mock diff --git a/.reuse/dep5 b/.reuse/dep5 index 4a780f5bc..5d01b077f 100644 --- a/.reuse/dep5 +++ b/.reuse/dep5 @@ -3,7 +3,11 @@ Upstream-Name: react-client Upstream-Contact: The HedgeDoc developers Source: https://github.com/hedgedoc/react-client -Files: public/api/* +Files: public/mock-backend/public/* +Copyright: 2021 The HedgeDoc developers (see AUTHORS file) +License: CC0-1.0 + +Files: public/mock-backend/api/* Copyright: 2021 The HedgeDoc developers (see AUTHORS file) License: CC0-1.0 @@ -15,11 +19,7 @@ Files: public/locales/* Copyright: 2021 The HedgeDoc developers (see AUTHORS file) License: CC-BY-SA-4.0 -Files: public/img/* -Copyright: 2021 The HedgeDoc developers (see AUTHORS file) -License: CC0-1.0 - -Files: public/img/highres.jpg +Files: public/mock-backend/public/img/highres.jpg Copyright: Vincent van Gogh License: CC0-1.0 @@ -27,10 +27,6 @@ Files: public/index.html Copyright: 2021 The HedgeDoc developers (see AUTHORS file) License: CC0-1.0 -Files: public/intro.md -Copyright: 2021 The HedgeDoc developers (see AUTHORS file) -License: CC0-1.0 - Files: public/robots.txt Copyright: 2021 The HedgeDoc developers (see AUTHORS file) License: CC0-1.0 diff --git a/README.md b/README.md index 5aeec7133..f64a5b2f8 100644 --- a/README.md +++ b/README.md @@ -65,4 +65,3 @@ You can inspect the generated production-bundle files to look for optimization i This will build the app in production mode and save it into the `build` folder. The production build is optimized for best performance, minimized and the filenames include a hash value of the content. Don't edit them by hand! - diff --git a/cypress/integration/fileUpload.spec.ts b/cypress/integration/fileUpload.spec.ts index 6d2cab99a..03eb4d625 100644 --- a/cypress/integration/fileUpload.spec.ts +++ b/cypress/integration/fileUpload.spec.ts @@ -31,7 +31,7 @@ describe('File upload', () => { beforeEach(() => { cy.intercept({ method: 'POST', - url: '/api/private/media/upload' + url: '/mock-backend/api/private/media/upload' }, { statusCode: 201, body: { @@ -86,7 +86,7 @@ describe('File upload', () => { it('upload fails', () => { cy.intercept({ method: 'POST', - url: '/api/private/media/upload' + url: '/mock-backend/api/private/media/upload' }, { statusCode: 400 }) diff --git a/cypress/integration/history.spec.ts b/cypress/integration/history.spec.ts index 60703a6f1..4bc485288 100644 --- a/cypress/integration/history.spec.ts +++ b/cypress/integration/history.spec.ts @@ -26,7 +26,7 @@ describe('History', () => { describe('Pinning', () => { describe('working', () => { beforeEach(() => { - cy.intercept('PUT', '/api/private/me/history/features', (req) => { + cy.intercept('PUT', '/mock-backend/api/private/me/history/features', (req) => { req.reply(200, req.body) }) }) @@ -60,7 +60,7 @@ describe('History', () => { describe('failing', () => { beforeEach(() => { - cy.intercept('PUT', '/api/private/me/history/features', { + cy.intercept('PUT', '/mock-backend/api/private/me/history/features', { statusCode: 401 }) }) diff --git a/cypress/integration/intro.spec.ts b/cypress/integration/intro.spec.ts index bdfb26b20..8206d92e0 100644 --- a/cypress/integration/intro.spec.ts +++ b/cypress/integration/intro.spec.ts @@ -7,7 +7,7 @@ /* eslint-disable @typescript-eslint/no-unsafe-call */ describe('Intro page', () => { beforeEach(() => { - cy.intercept('/intro.md', 'test content') + cy.intercept('/mock-backend/public/intro.md', 'test content') cy.visit('/') }) diff --git a/cypress/integration/profile.spec.ts b/cypress/integration/profile.spec.ts index dd4de202c..f053aed11 100644 --- a/cypress/integration/profile.spec.ts +++ b/cypress/integration/profile.spec.ts @@ -7,7 +7,7 @@ describe('profile page', () => { beforeEach(() => { cy.intercept({ - url: '/api/private/tokens', + url: '/mock-backend/api/private/tokens', method: 'GET' }, { body: [ @@ -18,7 +18,7 @@ describe('profile page', () => { ] }) cy.intercept({ - url: '/api/private/tokens', + url: '/mock-backend/api/private/tokens', method: 'POST' }, { body: { @@ -28,7 +28,7 @@ describe('profile page', () => { } }) cy.intercept({ - url: '/api/private/tokens/1601991518', + url: '/mock-backend/api/private/tokens/1601991518', method: 'DELETE' }, { body: [] diff --git a/cypress/integration/signInButton.spec.ts b/cypress/integration/signInButton.spec.ts index 0c27c4f87..cb7141c4d 100644 --- a/cypress/integration/signInButton.spec.ts +++ b/cypress/integration/signInButton.spec.ts @@ -83,7 +83,7 @@ describe('When logged-out ', () => { cy.get('[data-cy=sign-in-button]') .should('be.visible') // The absolute URL is used because it is defined as API base URL absolute. - .should('have.attr', 'href', 'http://127.0.0.1:3001/api/private/auth/saml') + .should('have.attr', 'href', '/mock-backend/api/private/auth/saml') }) }) diff --git a/cypress/support/config.ts b/cypress/support/config.ts index 9994c900e..68037d069 100644 --- a/cypress/support/config.ts +++ b/cypress/support/config.ts @@ -17,7 +17,7 @@ export const banner = { export const branding = { name: 'DEMO Corp', - logo: '/img/demo.png' + logo: '/mock-backend/public/img/demo.png' } export const authProviders = { @@ -63,7 +63,7 @@ export const config = { } Cypress.Commands.add('loadConfig', (additionalConfig?: Partial) => { - return cy.intercept('/api/private/config', { + return cy.intercept('/mock-backend/api/private/config', { statusCode: 200, body: { ...config, diff --git a/cypress/support/visit-test-editor.ts b/cypress/support/visit-test-editor.ts index 6ded85ffa..5ad10c8da 100644 --- a/cypress/support/visit-test-editor.ts +++ b/cypress/support/visit-test-editor.ts @@ -17,7 +17,7 @@ Cypress.Commands.add('visitTestEditor', (query?: string) => { }) beforeEach(() => { - cy.intercept(`/api/private/notes/${ testNoteId }-get`, { + cy.intercept(`/mock-backend/api/private/notes/${ testNoteId }-get`, { "content": "", "metadata": { "id": "ABC11", diff --git a/package.json b/package.json index 566871fd1..0e10a5009 100644 --- a/package.json +++ b/package.json @@ -109,12 +109,13 @@ }, "scripts": { "start": "cross-env PORT=3001 craco start", - "start:test": "cross-env PORT=3001 REACT_APP_TEST_MODE=true craco start", - "start:for-real-backend": "cross-env REACT_APP_BACKEND=http://localhost:3000 yarn start", + "start:test": "cross-env REACT_APP_TEST_MODE=true yarn start", + "start:for-real-backend": "cross-env REACT_APP_BACKEND_BASE_URL=http://localhost:3000 yarn start", "serve:build": "http-server build/ -s -p 3001 -P \"http://localhost:3001?\"", "build:test": "cross-env REACT_APP_TEST_MODE=true craco build", - "build:production": "craco build", - "analyze": "cross-env ANALYZE=true craco build", + "build:mock": "cross-env craco build", + "build:production": "bash -c \"[[ -v REACT_APP_BACKEND_BASE_URL ]]\" && (cross-env craco build) || (echo -e '\n==============\nREACT_APP_BACKEND_BASE_URL not set.\nUse this task only if you want to create a production build with a real backend. Otherwise use build:mock\n==============\n'; exit 1)", + "analyze": "cross-env ANALYZE=true yarn build:mock", "test": "craco test", "lint": "eslint --max-warnings=0 --ext .ts,.tsx src", "eject": "react-scripts eject", diff --git a/public/api/private/notes/features-get b/public/api/private/notes/features-get deleted file mode 100644 index e6b8c5d1e..000000000 --- a/public/api/private/notes/features-get +++ /dev/null @@ -1,18 +0,0 @@ -{ - "content": "---\ntitle: Features\ndescription: Many features, such wow!\nrobots: noindex\ntags:\n - hedgedoc\n - demo\n - react\nopengraph:\n title: Features\n---\n# Embedding demo\n[TOC]\n\n## markmap\n\n\n```markmap\n# MarkMap\n\n## Pro\n\n### written in typescript\n\n## Cons\n\n### must redeclare types\n```\n\n## Vega-Lite\n\n```vega-lite\n\n{\n \"$schema\": \"https://vega.github.io/schema/vega-lite/v5.json\",\n \"description\": \"Reproducing http://robslink.com/SAS/democd91/pyramid_pie.htm\",\n \"data\": {\n \"values\": [\n {\"category\": \"Sky\", \"value\": 75, \"order\": 3},\n {\"category\": \"Shady side of a pyramid\", \"value\": 10, \"order\": 1},\n {\"category\": \"Sunny side of a pyramid\", \"value\": 15, \"order\": 2}\n ]\n },\n \"mark\": {\"type\": \"arc\", \"outerRadius\": 80},\n \"encoding\": {\n \"theta\": {\n \"field\": \"value\", \"type\": \"quantitative\",\n \"scale\": {\"range\": [2.35619449, 8.639379797]},\n \"stack\": true\n },\n \"color\": {\n \"field\": \"category\", \"type\": \"nominal\",\n \"scale\": {\n \"domain\": [\"Sky\", \"Shady side of a pyramid\", \"Sunny side of a pyramid\"],\n \"range\": [\"#416D9D\", \"#674028\", \"#DEAC58\"]\n },\n \"legend\": {\n \"orient\": \"none\",\n \"title\": null,\n \"columns\": 1,\n \"legendX\": 200,\n \"legendY\": 80\n }\n },\n \"order\": {\n \"field\": \"order\"\n }\n },\n \"view\": {\"stroke\": null}\n}\n\n\n```\n\n## GraphViz\n\n```graphviz\ngraph {\n a -- b\n a -- b\n b -- a [color=blue]\n}\n```\n\n```graphviz\ndigraph structs {\n node [shape=record];\n struct1 [label=\" left| mid\ dle| right\"];\n struct2 [label=\" one| two\"];\n struct3 [label=\"hello\nworld |{ b |{c| d|e}| f}| g | h\"];\n struct1:f1 -> struct2:f0;\n struct1:f2 -> struct3:here;\n}\n```\n\n```graphviz\ndigraph G {\n main -> parse -> execute;\n main -> init;\n main -> cleanup;\n execute -> make_string;\n execute -> printf\n init -> make_string;\n main -> printf;\n execute -> compare;\n}\n```\n\n```graphviz\ndigraph D {\n node [fontname=\"Arial\"];\n node_A [shape=record label=\"shape=record|{above|middle|below}|right\"];\n node_B [shape=plaintext label=\"shape=plaintext|{curly|braces and|bars without}|effect\"];\n}\n```\n\n```graphviz\ndigraph D {\n A -> {B, C, D} -> {F}\n}\n```\n\n## High Res Image\n\n![Wheat Field with Cypresses](/img/highres.jpg)\n\n## Sequence Diagram (deprecated)\n\n```sequence\nTitle: Here is a title\nnote over A: asdd\nA->B: Normal line\nB-->C: Dashed line\nC->>D: Open arrow\nD-->>A: Dashed open arrow\nparticipant IOOO\n```\n\n## Mermaid\n\n```mermaid\ngantt\n title A Gantt Diagram\n\n section Section\n A task: a1, 2014-01-01, 30d\n Another task: after a1, 20d\n\n section Another\n Task in sec: 2014-01-12, 12d\n Another task: 24d\n```\n\n## Flowchart\n\n```flow\nst=>start: Start\ne=>end: End\nop=>operation: My Operation\nop2=>operation: lalala\ncond=>condition: Yes or No?\n\nst->op->op2->cond\ncond(yes)->e\ncond(no)->op2\n```\n\n## ABC\n\n```abc\nX:1\nT:Speed the Plough\nM:4/4\nC:Trad.\nK:G\n|:GABc dedB|dedB dedB|c2ec B2dB|c2A2 A2BA|\nGABc dedB|dedB dedB|c2ec B2dB|A2F2 G4:|\n|:g2gf gdBd|g2f2 e2d2|c2ec B2dB|c2A2 A2df|\ng2gf g2Bd|g2f2 e2d2|c2ec B2dB|A2F2 G4:|\n```\n\n## CSV\n\n```csv delimiter=; header\nUsername; Identifier;First name;Last name\n\"booker12; rbooker\";9012;Rachel;Booker\ngrey07;2070;Laura;Grey\njohnson81;4081;Craig;Johnson\njenkins46;9346;Mary;Jenkins\nsmith79;5079;Jamie;Smith\n```\n\n## some plain text\n\nLorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.\n\n## KaTeX\nYou can render *LaTeX* mathematical expressions using **KaTeX**, as on [math.stackexchange.com](https://math.stackexchange.com/):\n\nThe *Gamma function* satisfying $\\Gamma(n) = (n-1)!\\quad\\forall n\\in\\mathbb N$ is via the Euler integral\n\n$$\nx = {-b \\pm \\sqrt{b^2-4ac} \\over 2a}.\n$$\n\n$$\n\\Gamma(z) = \\int_0^\\infty t^{z-1}e^{-t}dt\\,.\n$$\n\n> More information about **LaTeX** mathematical expressions [here](https://meta.math.stackexchange.com/questions/5020/mathjax-basic-tutorial-and-quick-reference).\n\n## Blockquote\n> Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.\n> Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.\n> [color=red] [name=John Doe] [time=2020-06-21 22:50]\n\n## Slideshare\n{%slideshare mazlan1/internet-of-things-the-tip-of-an-iceberg %}\n\n## Gist\nhttps://gist.github.com/schacon/1\n\n## YouTube\nhttps://www.youtube.com/watch?v=YE7VzlLtp-4\n\n## Vimeo\nhttps://vimeo.com/23237102\n\n## Asciinema\nhttps://asciinema.org/a/117928\n\n## PDF\n{%pdf https://www.w3.org/WAI/ER/tests/xhtml/testfiles/resources/pdf/dummy.pdf %}\n\n## Code highlighting\n```js=\nvar s = \"JavaScript syntax highlighting\";\nalert(s);\nfunction $initHighlight(block, cls) {\n try {\n if (cls.search(/\\bno\\-highlight\\b/) != -1)\n return process(block, true, 0x0F) +\n ' class=\"\"';\n } catch (e) {\n /* handle exception */\n }\n for (var i = 0 / 2; i < classes.length; i++) {\n if (checkCondition(classes[i]) === undefined)\n return /\\d+[\\s/]/g;\n }\n}\n```\n\n## PlantUML\n```plantuml\n@startuml\nparticipant Alice\nparticipant \"The **Famous** Bob\" as Bob\n\nAlice -> Bob : hello --there--\n... Some ~~long delay~~ ...\nBob -> Alice : ok\nnote left\n This is **bold**\n This is //italics//\n This is \"\"monospaced\"\"\n This is --stroked--\n This is __underlined__\n This is ~~waved~~\nend note\n\nAlice -> Bob : A //well formatted// message\nnote right of Alice\n This is displayed\n __left of__ Alice.\nend note\nnote left of Bob\n This is displayed\n **left of Alice Bob**.\nend note\nnote over Alice, Bob\n This is hosted by \nend note\n@enduml\n```\n\n## ToDo List\n\n- [ ] ToDos\n - [X] Buy some salad\n - [ ] Brush teeth\n - [x] Drink some water\n - [ ] **Click my box** and see the source code, if you're allowed to edit!\n\n", - "metadata": { - "id": "ABC2", - "alias": "features", - "version": 2, - "viewCount": 0, - "updateTime": "2021-04-24T09:27:51.000Z", - "updateUser": { - "userName": "test", - "displayName": "Testy", - "photo": "", - "email": "" - }, - "createTime": "2021-04-24T09:27:51.000Z", - "editedBy": [] - } -} diff --git a/public/img/demo.png.license b/public/img/demo.png.license deleted file mode 100644 index 078e5a9ac..000000000 --- a/public/img/demo.png.license +++ /dev/null @@ -1,3 +0,0 @@ -SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file) - -SPDX-License-Identifier: CC0-1.0 diff --git a/public/index.html b/public/index.html index 62c7cb83d..18874c0e9 100644 --- a/public/index.html +++ b/public/index.html @@ -3,19 +3,20 @@ HedgeDoc - - - - - - + + + + + + - + + diff --git a/public/api/private/config b/public/mock-backend/api/private/config similarity index 87% rename from public/api/private/config rename to public/mock-backend/api/private/config index d72f9942e..15129549c 100644 --- a/public/api/private/config +++ b/public/mock-backend/api/private/config @@ -16,11 +16,11 @@ "allowRegister": true, "branding": { "name": "DEMO Corp", - "logo": "/img/demo.png" + "logo": "/mock-backend/public/img/demo.png" }, "banner": { - "text": "This is the test banner text", - "timestamp": "2020-05-22T20:46:08.962Z" + "text": "This is the test banner text", + "timestamp": "2020-05-22T20:46:08.962Z" }, "customAuthNames": { "ldap": "FooBar", diff --git a/public/api/private/me-get b/public/mock-backend/api/private/me-get similarity index 64% rename from public/api/private/me-get rename to public/mock-backend/api/private/me-get index d3c9ef7ac..7d2100883 100644 --- a/public/api/private/me-get +++ b/public/mock-backend/api/private/me-get @@ -1,6 +1,6 @@ { "id": "mockUser", - "photo": "/img/avatar.png", + "photo": "/mock-backend/public/img/avatar.png", "name": "Test", "status": "ok", "provider": "internal" diff --git a/public/api/private/me/history b/public/mock-backend/api/private/me/history similarity index 100% rename from public/api/private/me/history rename to public/mock-backend/api/private/me/history diff --git a/public/api/private/notes/banner-get b/public/mock-backend/api/private/notes/banner-get similarity index 100% rename from public/api/private/notes/banner-get rename to public/mock-backend/api/private/notes/banner-get diff --git a/public/mock-backend/api/private/notes/features-get b/public/mock-backend/api/private/notes/features-get new file mode 100644 index 000000000..eb7dfe565 --- /dev/null +++ b/public/mock-backend/api/private/notes/features-get @@ -0,0 +1,18 @@ +{ + "content": "---\ntitle: Features\ndescription: Many features, such wow!\nrobots: noindex\ntags:\n - hedgedoc\n - demo\n - react\nopengraph:\n title: Features\n---\n# Embedding demo\n[TOC]\n\n## markmap\n\n\n```markmap\n# MarkMap\n\n## Pro\n\n### written in typescript\n\n## Cons\n\n### must redeclare types\n```\n\n## Vega-Lite\n\n```vega-lite\n\n{\n \"$schema\": \"https://vega.github.io/schema/vega-lite/v5.json\",\n \"description\": \"Reproducing http://robslink.com/SAS/democd91/pyramid_pie.htm\",\n \"data\": {\n \"values\": [\n {\"category\": \"Sky\", \"value\": 75, \"order\": 3},\n {\"category\": \"Shady side of a pyramid\", \"value\": 10, \"order\": 1},\n {\"category\": \"Sunny side of a pyramid\", \"value\": 15, \"order\": 2}\n ]\n },\n \"mark\": {\"type\": \"arc\", \"outerRadius\": 80},\n \"encoding\": {\n \"theta\": {\n \"field\": \"value\", \"type\": \"quantitative\",\n \"scale\": {\"range\": [2.35619449, 8.639379797]},\n \"stack\": true\n },\n \"color\": {\n \"field\": \"category\", \"type\": \"nominal\",\n \"scale\": {\n \"domain\": [\"Sky\", \"Shady side of a pyramid\", \"Sunny side of a pyramid\"],\n \"range\": [\"#416D9D\", \"#674028\", \"#DEAC58\"]\n },\n \"legend\": {\n \"orient\": \"none\",\n \"title\": null,\n \"columns\": 1,\n \"legendX\": 200,\n \"legendY\": 80\n }\n },\n \"order\": {\n \"field\": \"order\"\n }\n },\n \"view\": {\"stroke\": null}\n}\n\n\n```\n\n## GraphViz\n\n```graphviz\ngraph {\n a -- b\n a -- b\n b -- a [color=blue]\n}\n```\n\n```graphviz\ndigraph structs {\n node [shape=record];\n struct1 [label=\" left| mid\ dle| right\"];\n struct2 [label=\" one| two\"];\n struct3 [label=\"hello\nworld |{ b |{c| d|e}| f}| g | h\"];\n struct1:f1 -> struct2:f0;\n struct1:f2 -> struct3:here;\n}\n```\n\n```graphviz\ndigraph G {\n main -> parse -> execute;\n main -> init;\n main -> cleanup;\n execute -> make_string;\n execute -> printf\n init -> make_string;\n main -> printf;\n execute -> compare;\n}\n```\n\n```graphviz\ndigraph D {\n node [fontname=\"Arial\"];\n node_A [shape=record label=\"shape=record|{above|middle|below}|right\"];\n node_B [shape=plaintext label=\"shape=plaintext|{curly|braces and|bars without}|effect\"];\n}\n```\n\n```graphviz\ndigraph D {\n A -> {B, C, D} -> {F}\n}\n```\n\n## High Res Image\n\n![Wheat Field with Cypresses](/mock-backend/public/img/highres.jpg)\n\n## Sequence Diagram (deprecated)\n\n```sequence\nTitle: Here is a title\nnote over A: asdd\nA->B: Normal line\nB-->C: Dashed line\nC->>D: Open arrow\nD-->>A: Dashed open arrow\nparticipant IOOO\n```\n\n## Mermaid\n\n```mermaid\ngantt\n title A Gantt Diagram\n\n section Section\n A task: a1, 2014-01-01, 30d\n Another task: after a1, 20d\n\n section Another\n Task in sec: 2014-01-12, 12d\n Another task: 24d\n```\n\n## Flowchart\n\n```flow\nst=>start: Start\ne=>end: End\nop=>operation: My Operation\nop2=>operation: lalala\ncond=>condition: Yes or No?\n\nst->op->op2->cond\ncond(yes)->e\ncond(no)->op2\n```\n\n## ABC\n\n```abc\nX:1\nT:Speed the Plough\nM:4/4\nC:Trad.\nK:G\n|:GABc dedB|dedB dedB|c2ec B2dB|c2A2 A2BA|\nGABc dedB|dedB dedB|c2ec B2dB|A2F2 G4:|\n|:g2gf gdBd|g2f2 e2d2|c2ec B2dB|c2A2 A2df|\ng2gf g2Bd|g2f2 e2d2|c2ec B2dB|A2F2 G4:|\n```\n\n## CSV\n\n```csv delimiter=; header\nUsername; Identifier;First name;Last name\n\"booker12; rbooker\";9012;Rachel;Booker\ngrey07;2070;Laura;Grey\njohnson81;4081;Craig;Johnson\njenkins46;9346;Mary;Jenkins\nsmith79;5079;Jamie;Smith\n```\n\n## some plain text\n\nLorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.\n\n## KaTeX\nYou can render *LaTeX* mathematical expressions using **KaTeX**, as on [math.stackexchange.com](https://math.stackexchange.com/):\n\nThe *Gamma function* satisfying $\\Gamma(n) = (n-1)!\\quad\\forall n\\in\\mathbb N$ is via the Euler integral\n\n$$\nx = {-b \\pm \\sqrt{b^2-4ac} \\over 2a}.\n$$\n\n$$\n\\Gamma(z) = \\int_0^\\infty t^{z-1}e^{-t}dt\\,.\n$$\n\n> More information about **LaTeX** mathematical expressions [here](https://meta.math.stackexchange.com/questions/5020/mathjax-basic-tutorial-and-quick-reference).\n\n## Blockquote\n> Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.\n> Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.\n> [color=red] [name=John Doe] [time=2020-06-21 22:50]\n\n## Slideshare\n{%slideshare mazlan1/internet-of-things-the-tip-of-an-iceberg %}\n\n## Gist\nhttps://gist.github.com/schacon/1\n\n## YouTube\nhttps://www.youtube.com/watch?v=YE7VzlLtp-4\n\n## Vimeo\nhttps://vimeo.com/23237102\n\n## Asciinema\nhttps://asciinema.org/a/117928\n\n## PDF\n{%pdf https://www.w3.org/WAI/ER/tests/xhtml/testfiles/resources/pdf/dummy.pdf %}\n\n## Code highlighting\n```js=\nvar s = \"JavaScript syntax highlighting\";\nalert(s);\nfunction $initHighlight(block, cls) {\n try {\n if (cls.search(/\\bno\\-highlight\\b/) != -1)\n return process(block, true, 0x0F) +\n ' class=\"\"';\n } catch (e) {\n /* handle exception */\n }\n for (var i = 0 / 2; i < classes.length; i++) {\n if (checkCondition(classes[i]) === undefined)\n return /\\d+[\\s/]/g;\n }\n}\n```\n\n## PlantUML\n```plantuml\n@startuml\nparticipant Alice\nparticipant \"The **Famous** Bob\" as Bob\n\nAlice -> Bob : hello --there--\n... Some ~~long delay~~ ...\nBob -> Alice : ok\nnote left\n This is **bold**\n This is //italics//\n This is \"\"monospaced\"\"\n This is --stroked--\n This is __underlined__\n This is ~~waved~~\nend note\n\nAlice -> Bob : A //well formatted// message\nnote right of Alice\n This is displayed\n __left of__ Alice.\nend note\nnote left of Bob\n This is displayed\n **left of Alice Bob**.\nend note\nnote over Alice, Bob\n This is hosted by \nend note\n@enduml\n```\n\n## ToDo List\n\n- [ ] ToDos\n - [X] Buy some salad\n - [ ] Brush teeth\n - [x] Drink some water\n - [ ] **Click my box** and see the source code, if you're allowed to edit!\n\n", + "metadata": { + "id": "ABC2", + "alias": "features", + "version": 2, + "viewCount": 0, + "updateTime": "2021-04-24T09:27:51.000Z", + "updateUser": { + "userName": "test", + "displayName": "Testy", + "photo": "", + "email": "" + }, + "createTime": "2021-04-24T09:27:51.000Z", + "editedBy": [] + } +} diff --git a/public/api/private/notes/features/revisions-list b/public/mock-backend/api/private/notes/features/revisions-list similarity index 100% rename from public/api/private/notes/features/revisions-list rename to public/mock-backend/api/private/notes/features/revisions-list diff --git a/public/api/private/notes/features/revisions/1598389571 b/public/mock-backend/api/private/notes/features/revisions/1598389571 similarity index 100% rename from public/api/private/notes/features/revisions/1598389571 rename to public/mock-backend/api/private/notes/features/revisions/1598389571 diff --git a/public/api/private/notes/features/revisions/1598390307 b/public/mock-backend/api/private/notes/features/revisions/1598390307 similarity index 100% rename from public/api/private/notes/features/revisions/1598390307 rename to public/mock-backend/api/private/notes/features/revisions/1598390307 diff --git a/public/api/private/notes/old-get b/public/mock-backend/api/private/notes/old-get similarity index 100% rename from public/api/private/notes/old-get rename to public/mock-backend/api/private/notes/old-get diff --git a/public/api/private/tokens b/public/mock-backend/api/private/tokens similarity index 100% rename from public/api/private/tokens rename to public/mock-backend/api/private/tokens diff --git a/public/api/private/users/dermolly b/public/mock-backend/api/private/users/dermolly similarity index 63% rename from public/api/private/users/dermolly rename to public/mock-backend/api/private/users/dermolly index 00ce647cf..c213e0a41 100644 --- a/public/api/private/users/dermolly +++ b/public/mock-backend/api/private/users/dermolly @@ -1,6 +1,6 @@ { "id": "dermolly", - "photo": "/img/avatar.png", + "photo": "/mock-backend/public/img/avatar.png", "name": "Philip", "status": "ok", "provider": "internal" diff --git a/public/api/private/users/emcrx b/public/mock-backend/api/private/users/emcrx similarity index 62% rename from public/api/private/users/emcrx rename to public/mock-backend/api/private/users/emcrx index f413b14a9..fb1a71149 100644 --- a/public/api/private/users/emcrx +++ b/public/mock-backend/api/private/users/emcrx @@ -1,6 +1,6 @@ { "id": "emcrx", - "photo": "/img/avatar.png", + "photo": "/mock-backend/public/img/avatar.png", "name": "Erik", "status": "ok", "provider": "internal" diff --git a/public/api/private/users/mrdrogdrog b/public/mock-backend/api/private/users/mrdrogdrog similarity index 64% rename from public/api/private/users/mrdrogdrog rename to public/mock-backend/api/private/users/mrdrogdrog index bdbf352bc..cff873548 100644 --- a/public/api/private/users/mrdrogdrog +++ b/public/mock-backend/api/private/users/mrdrogdrog @@ -1,6 +1,6 @@ { "id": "mrdrogdrog", - "photo": "/img/avatar.png", + "photo": "/mock-backend/public/img/avatar.png", "name": "Tilman", "status": "ok", "provider": "internal" diff --git a/public/img/avatar.png b/public/mock-backend/public/img/avatar.png similarity index 100% rename from public/img/avatar.png rename to public/mock-backend/public/img/avatar.png diff --git a/public/img/demo.png b/public/mock-backend/public/img/demo.png similarity index 100% rename from public/img/demo.png rename to public/mock-backend/public/img/demo.png diff --git a/public/img/highres.jpg b/public/mock-backend/public/img/highres.jpg similarity index 100% rename from public/img/highres.jpg rename to public/mock-backend/public/img/highres.jpg diff --git a/public/intro.md b/public/mock-backend/public/intro.md similarity index 67% rename from public/intro.md rename to public/mock-backend/public/intro.md index 70ff31147..885360038 100644 --- a/public/intro.md +++ b/public/mock-backend/public/intro.md @@ -2,4 +2,4 @@ What you see is an UI-Test! It's filled with dummy data, not connected to a backend and no data will be saved. ::: -![HedgeDoc Screenshot](screenshot.png) +![HedgeDoc Screenshot](/mock-backend/public/screenshot.png) diff --git a/public/screenshot.png b/public/mock-backend/public/screenshot.png similarity index 100% rename from public/screenshot.png rename to public/mock-backend/public/screenshot.png diff --git a/public/screenshot.png.license b/public/screenshot.png.license deleted file mode 100644 index 078e5a9ac..000000000 --- a/public/screenshot.png.license +++ /dev/null @@ -1,3 +0,0 @@ -SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file) - -SPDX-License-Identifier: CC0-1.0 diff --git a/src/components/application-loader/application-loader.tsx b/src/components/application-loader/application-loader.tsx index 634746c73..e31e1b1ea 100644 --- a/src/components/application-loader/application-loader.tsx +++ b/src/components/application-loader/application-loader.tsx @@ -1,21 +1,24 @@ /* - SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file) - - SPDX-License-Identifier: AGPL-3.0-only + * SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file) + * + * SPDX-License-Identifier: AGPL-3.0-only */ import React, { Suspense, useCallback, useEffect, useState } from 'react' -import { useFrontendBaseUrl } from '../../hooks/common/use-frontend-base-url' +import { useBackendBaseUrl } from '../../hooks/common/use-backend-base-url' import './application-loader.scss' import { createSetUpTaskList, InitTask } from './initializers' import { LoadingScreen } from './loading-screen' +import { useCustomizeAssetsUrl } from '../../hooks/common/use-customize-assets-url' +import { useFrontendAssetsUrl } from '../../hooks/common/use-frontend-assets-url' export const ApplicationLoader: React.FC = ({ children }) => { - const frontendUrl = useFrontendBaseUrl() + const frontendAssetsUrl = useFrontendAssetsUrl() + const backendBaseUrl = useBackendBaseUrl() + const customizeAssetsUrl = useCustomizeAssetsUrl() - const setUpTasks = useCallback(() => { - return createSetUpTaskList(frontendUrl) - }, [frontendUrl]) + const setUpTasks = useCallback(() => createSetUpTaskList(frontendAssetsUrl, customizeAssetsUrl, backendBaseUrl), + [backendBaseUrl, customizeAssetsUrl, frontendAssetsUrl]) const [failedTitle, setFailedTitle] = useState('') const [doneTasks, setDoneTasks] = useState(0) diff --git a/src/components/application-loader/initializers/configLoader.ts b/src/components/application-loader/initializers/fetch-frontend-config.ts similarity index 68% rename from src/components/application-loader/initializers/configLoader.ts rename to src/components/application-loader/initializers/fetch-frontend-config.ts index 794feccd2..b3e82c305 100644 --- a/src/components/application-loader/initializers/configLoader.ts +++ b/src/components/application-loader/initializers/fetch-frontend-config.ts @@ -5,16 +5,10 @@ */ import { getConfig } from '../../../api/config' -import { setApiUrl } from '../../../redux/api-url/methods' import { setBanner } from '../../../redux/banner/methods' import { setConfig } from '../../../redux/config/methods' -import { getAndSetUser } from '../../login-page/auth/utils' - -export const loadAllConfig: (baseUrl: string) => Promise = async (baseUrl) => { - setApiUrl({ - apiUrl: (process.env.REACT_APP_BACKEND || baseUrl) + '/api/private' - }) +export const fetchFrontendConfig = async (): Promise => { const config = await getConfig() if (!config) { return Promise.reject(new Error('Config empty!')) @@ -29,6 +23,4 @@ export const loadAllConfig: (baseUrl: string) => Promise = async (baseUrl) show: banner.text !== '' && banner.timestamp !== lastAcknowledgedTimestamp }) } - - await getAndSetUser() } diff --git a/src/components/application-loader/initializers/i18n.ts b/src/components/application-loader/initializers/i18n.ts index f8f756ecf..952fc4987 100644 --- a/src/components/application-loader/initializers/i18n.ts +++ b/src/components/application-loader/initializers/i18n.ts @@ -10,7 +10,7 @@ import Backend from 'i18next-http-backend' import { Settings } from 'luxon' import { initReactI18next } from 'react-i18next' -export const setUpI18n = async (): Promise => { +export const setUpI18n = async (frontendAssetsUrl: string): Promise => { await i18n .use(Backend) .use(LanguageDetector) @@ -19,7 +19,7 @@ export const setUpI18n = async (): Promise => { fallbackLng: 'en', debug: process.env.NODE_ENV !== 'production', backend: { - loadPath: '/locales/{{lng}}.json' + loadPath: `${ frontendAssetsUrl }/locales/{{lng}}.json` }, interpolation: { diff --git a/src/components/application-loader/initializers/index.ts b/src/components/application-loader/initializers/index.ts index 0c014aa46..b87ab4d3a 100644 --- a/src/components/application-loader/initializers/index.ts +++ b/src/components/application-loader/initializers/index.ts @@ -4,9 +4,11 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -import { loadAllConfig } from './configLoader' import { setUpI18n } from './i18n' import { refreshHistoryState } from '../../../redux/history/methods' +import { setApiUrl } from '../../../redux/api-url/methods' +import { fetchAndSetUser } from '../../login-page/auth/utils' +import { fetchFrontendConfig } from './fetch-frontend-config' const customDelay: () => Promise = async () => { if (window.localStorage.getItem('customDelay')) { @@ -21,13 +23,20 @@ export interface InitTask { task: Promise } -export const createSetUpTaskList = (baseUrl: string): InitTask[] => { +export const createSetUpTaskList = (frontendAssetsUrl: string, customizeAssetsUrl: string, backendBaseUrl: string): InitTask[] => { + setApiUrl({ + apiUrl: `${ backendBaseUrl }/api/private` + }) + return [{ name: 'Load Translations', - task: setUpI18n() + task: setUpI18n(frontendAssetsUrl) }, { name: 'Load config', - task: loadAllConfig(baseUrl) + task: fetchFrontendConfig() + }, { + name: 'Fetch user information', + task: fetchAndSetUser() }, { name: 'Load history state', task: refreshHistoryState() diff --git a/src/components/intro-page/hooks/use-intro-page-content.ts b/src/components/intro-page/hooks/use-intro-page-content.ts index 170328abb..f08733a26 100644 --- a/src/components/intro-page/hooks/use-intro-page-content.ts +++ b/src/components/intro-page/hooks/use-intro-page-content.ts @@ -6,8 +6,8 @@ import { useEffect, useState } from 'react' import { useTranslation } from 'react-i18next' -import { getFrontPageContent } from '../requests' -import { useFrontendBaseUrl } from '../../../hooks/common/use-frontend-base-url' +import { fetchFrontPageContent } from '../requests' +import { useCustomizeAssetsUrl } from '../../../hooks/common/use-customize-assets-url' const MARKDOWN_WHILE_LOADING = ':zzz: {message}' const MARKDOWN_IF_ERROR = ':::danger\n' + @@ -17,13 +17,13 @@ const MARKDOWN_IF_ERROR = ':::danger\n' + export const useIntroPageContent = (): string => { const { t } = useTranslation() const [content, setContent] = useState(() => MARKDOWN_WHILE_LOADING.replace('{message}', t('landing.intro.markdownWhileLoading'))) - const frontendBaseUrl = useFrontendBaseUrl() + const customizeAssetsUrl = useCustomizeAssetsUrl() useEffect(() => { - getFrontPageContent(frontendBaseUrl) + fetchFrontPageContent(customizeAssetsUrl) .then((content) => setContent(content)) .catch(() => setContent(MARKDOWN_IF_ERROR.replace('{message}', t('landing.intro.markdownLoadingError')))) - }, [frontendBaseUrl, t]) + }, [customizeAssetsUrl, t]) return content } diff --git a/src/components/intro-page/requests.ts b/src/components/intro-page/requests.ts index e420b536d..ef27e4d3b 100644 --- a/src/components/intro-page/requests.ts +++ b/src/components/intro-page/requests.ts @@ -6,8 +6,8 @@ import { defaultFetchConfig, expectResponseCode } from '../../api/utils' -export const getFrontPageContent = async (baseUrl: string): Promise => { - const response = await fetch(baseUrl + '/intro.md', { +export const fetchFrontPageContent = async (customizeAssetsUrl: string): Promise => { + const response = await fetch(customizeAssetsUrl + '/intro.md', { ...defaultFetchConfig, method: 'GET' }) diff --git a/src/components/landing-layout/footer/powered-by-links.tsx b/src/components/landing-layout/footer/powered-by-links.tsx index 7a5252a77..b84806899 100644 --- a/src/components/landing-layout/footer/powered-by-links.tsx +++ b/src/components/landing-layout/footer/powered-by-links.tsx @@ -26,7 +26,7 @@ export const PoweredByLinks: React.FC = () => {  |  - + { specialUrls.map(([i18nKey, href]) => diff --git a/src/components/login-page/auth/utils.ts b/src/components/login-page/auth/utils.ts index 19e438d0d..5ffc621b5 100644 --- a/src/components/login-page/auth/utils.ts +++ b/src/components/login-page/auth/utils.ts @@ -7,7 +7,7 @@ import { getMe } from '../../../api/me' import { setUser } from '../../../redux/user/methods' -export const getAndSetUser: () => (Promise) = async () => { +export const fetchAndSetUser: () => (Promise) = async () => { const me = await getMe() setUser({ id: me.id, diff --git a/src/components/login-page/auth/via-internal.tsx b/src/components/login-page/auth/via-internal.tsx index 2a3c65877..e0fc1801b 100644 --- a/src/components/login-page/auth/via-internal.tsx +++ b/src/components/login-page/auth/via-internal.tsx @@ -12,7 +12,7 @@ import { Link } from 'react-router-dom' import { doInternalLogin } from '../../../api/auth' import { ApplicationState } from '../../../redux' import { ShowIf } from '../../common/show-if/show-if' -import { getAndSetUser } from './utils' +import { fetchAndSetUser } from './utils' export const ViaInternal: React.FC = () => { const { t } = useTranslation() @@ -23,7 +23,7 @@ export const ViaInternal: React.FC = () => { const onLoginSubmit = useCallback((event: FormEvent) => { doInternalLogin(username, password) - .then(() => getAndSetUser()) + .then(() => fetchAndSetUser()) .catch(() => setError(true)) event.preventDefault() }, [username, password]) diff --git a/src/components/login-page/auth/via-ldap.tsx b/src/components/login-page/auth/via-ldap.tsx index fdff5b24e..a357494a9 100644 --- a/src/components/login-page/auth/via-ldap.tsx +++ b/src/components/login-page/auth/via-ldap.tsx @@ -1,7 +1,7 @@ /* - SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file) - - SPDX-License-Identifier: AGPL-3.0-only + * SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file) + * + * SPDX-License-Identifier: AGPL-3.0-only */ import React, { FormEvent, useCallback, useState } from 'react' @@ -11,7 +11,7 @@ import { Trans, useTranslation } from 'react-i18next' import { useSelector } from 'react-redux' import { doLdapLogin } from '../../../api/auth' import { ApplicationState } from '../../../redux' -import { getAndSetUser } from './utils' +import { fetchAndSetUser } from './utils' export const ViaLdap: React.FC = () => { const { t } = useTranslation() @@ -25,7 +25,7 @@ export const ViaLdap: React.FC = () => { const onLoginSubmit = useCallback((event: FormEvent) => { doLdapLogin(username, password) - .then(() => getAndSetUser()) + .then(() => fetchAndSetUser()) .catch(() => setError(true)) event.preventDefault() }, [username, password]) diff --git a/src/components/login-page/auth/via-openid.tsx b/src/components/login-page/auth/via-openid.tsx index efa57b10f..43c8a3e49 100644 --- a/src/components/login-page/auth/via-openid.tsx +++ b/src/components/login-page/auth/via-openid.tsx @@ -1,14 +1,14 @@ /* - SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file) - - SPDX-License-Identifier: AGPL-3.0-only + * SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file) + * + * SPDX-License-Identifier: AGPL-3.0-only */ import React, { FormEvent, useState } from 'react' import { Alert, Button, Card, Form } from 'react-bootstrap' import { Trans, useTranslation } from 'react-i18next' import { doOpenIdLogin } from '../../../api/auth' -import { getAndSetUser } from './utils' +import { fetchAndSetUser } from './utils' export const ViaOpenId: React.FC = () => { useTranslation() @@ -16,7 +16,7 @@ export const ViaOpenId: React.FC = () => { const [error, setError] = useState(false) const doAsyncLogin: (() => Promise) = async () => { await doOpenIdLogin(openId) - await getAndSetUser() + await fetchAndSetUser() } const onFormSubmit = (event: FormEvent) => { diff --git a/src/components/markdown-renderer/replace-components/graphviz/graphviz-frame.tsx b/src/components/markdown-renderer/replace-components/graphviz/graphviz-frame.tsx index fb79dc849..f6691223e 100644 --- a/src/components/markdown-renderer/replace-components/graphviz/graphviz-frame.tsx +++ b/src/components/markdown-renderer/replace-components/graphviz/graphviz-frame.tsx @@ -7,6 +7,7 @@ import React, { Fragment, useCallback, useEffect, useRef, useState } from 'react' import { Alert } from 'react-bootstrap' import { ShowIf } from '../../../common/show-if/show-if' +import { useFrontendBaseUrl } from '../../../../hooks/common/use-frontend-base-url' export interface GraphvizFrameProps { code: string @@ -26,6 +27,8 @@ export const GraphvizFrame: React.FC = ({ code }) => { .forEach(child => child.remove()) }, []) + const frontendBaseUrl = useFrontendBaseUrl() + useEffect(() => { if (!container.current) { return @@ -34,7 +37,7 @@ export const GraphvizFrame: React.FC = ({ code }) => { import(/* webpackChunkName: "d3-graphviz" */'@hpcc-js/wasm') .then((wasmPlugin) => { - wasmPlugin.wasmFolder('/static/js') + wasmPlugin.wasmFolder(`${ frontendBaseUrl }/static/js`) }) .then(() => import(/* webpackChunkName: "d3-graphviz" */ 'd3-graphviz')) .then((graphvizImport) => { @@ -53,7 +56,7 @@ export const GraphvizFrame: React.FC = ({ code }) => { .catch(() => { console.error('error while loading graphviz') }) - }, [code, error, showError]) + }, [code, error, frontendBaseUrl, showError]) return ( diff --git a/src/components/profile-page/settings/profile-display-name.tsx b/src/components/profile-page/settings/profile-display-name.tsx index 012b0e9e1..3bc7f2878 100644 --- a/src/components/profile-page/settings/profile-display-name.tsx +++ b/src/components/profile-page/settings/profile-display-name.tsx @@ -1,7 +1,7 @@ /* - SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file) - - SPDX-License-Identifier: AGPL-3.0-only + * SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file) + * + * SPDX-License-Identifier: AGPL-3.0-only */ import React, { ChangeEvent, FormEvent, useEffect, useState } from 'react' @@ -10,7 +10,7 @@ import { Trans, useTranslation } from 'react-i18next' import { useSelector } from 'react-redux' import { updateDisplayName } from '../../../api/me' import { ApplicationState } from '../../../redux' -import { getAndSetUser } from '../../login-page/auth/utils' +import { fetchAndSetUser } from '../../login-page/auth/utils' export const ProfileDisplayName: React.FC = () => { const regexInvalidDisplayName = /^\s*$/ @@ -37,7 +37,7 @@ export const ProfileDisplayName: React.FC = () => { const doAsyncChange = async () => { await updateDisplayName(displayName) - await getAndSetUser() + await fetchAndSetUser() } const changeNameSubmit = (event: FormEvent) => { diff --git a/src/components/register-page/register-page.tsx b/src/components/register-page/register-page.tsx index 6b7450a8a..525a2bb9e 100644 --- a/src/components/register-page/register-page.tsx +++ b/src/components/register-page/register-page.tsx @@ -1,7 +1,7 @@ /* - SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file) - - SPDX-License-Identifier: AGPL-3.0-only + * SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file) + * + * SPDX-License-Identifier: AGPL-3.0-only */ import React, { FormEvent, Fragment, useCallback, useEffect, useState } from 'react' @@ -13,8 +13,7 @@ import { doInternalRegister } from '../../api/auth' import { ApplicationState } from '../../redux' import { TranslatedExternalLink } from '../common/links/translated-external-link' import { ShowIf } from '../common/show-if/show-if' -import { getAndSetUser } from '../login-page/auth/utils' -import { SpecialUrls } from '../../api/config/types' +import { fetchAndSetUser } from '../login-page/auth/utils' export enum RegisterError { NONE = 'none', @@ -25,7 +24,7 @@ export enum RegisterError { export const RegisterPage: React.FC = () => { const { t } = useTranslation() const allowRegister = useSelector((state: ApplicationState) => state.config.allowRegister) - const specialUrls: SpecialUrls = useSelector((state: ApplicationState) => state.config.specialUrls) + const specialUrls = useSelector((state: ApplicationState) => state.config.specialUrls) const userExists = useSelector((state: ApplicationState) => !!state.user) const [username, setUsername] = useState('') @@ -36,7 +35,7 @@ export const RegisterPage: React.FC = () => { const doRegisterSubmit = useCallback((event: FormEvent) => { doInternalRegister(username, password) - .then(() => getAndSetUser()) + .then(() => fetchAndSetUser()) .catch((err: Error) => { console.error(err) setError(err.message === RegisterError.USERNAME_EXISTING ? err.message : RegisterError.OTHER) @@ -61,83 +60,84 @@ export const RegisterPage: React.FC = () => { } return -
-

- +
+

+ - +
- - + + setUsername(event.target.value) } placeholder={ t('login.auth.username') } - className='bg-dark text-light' - autoComplete='username' + className="bg-dark text-light" + autoComplete="username" autoFocus={ true } required /> - + - - + + = 8 } onChange={ (event) => setPassword(event.target.value) } placeholder={ t('login.auth.password') } - className='bg-dark text-light' + className="bg-dark text-light" minLength={ 8 } - autoComplete='new-password' + autoComplete="new-password" required /> - + - - + + setPasswordAgain(event.target.value) } placeholder={ t('login.register.passwordAgain') } - className='bg-dark text-light' - autoComplete='new-password' + className="bg-dark text-light" + autoComplete="new-password" required /> - - + +
    - +
  • - +
  • - +
  • - +

- +
diff --git a/src/hooks/common/use-backend-base-url.ts b/src/hooks/common/use-backend-base-url.ts new file mode 100644 index 000000000..11f0487b5 --- /dev/null +++ b/src/hooks/common/use-backend-base-url.ts @@ -0,0 +1,9 @@ +/* + * SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file) + * + * SPDX-License-Identifier: AGPL-3.0-only + */ + +export const useBackendBaseUrl = (): string => { + return process.env.REACT_APP_BACKEND_BASE_URL ?? '/mock-backend' +} diff --git a/src/hooks/common/use-customize-assets-url.ts b/src/hooks/common/use-customize-assets-url.ts new file mode 100644 index 000000000..84aca61d5 --- /dev/null +++ b/src/hooks/common/use-customize-assets-url.ts @@ -0,0 +1,12 @@ +/* + * SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file) + * + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { useBackendBaseUrl } from './use-backend-base-url' + +export const useCustomizeAssetsUrl = (): string => { + const backendBaseUrl = useBackendBaseUrl() + return (process.env.REACT_APP_CUSTOMIZE_ASSETS_URL || `${ backendBaseUrl }/public`) +} diff --git a/src/hooks/common/use-frontend-assets-url.ts b/src/hooks/common/use-frontend-assets-url.ts new file mode 100644 index 000000000..99e0440a0 --- /dev/null +++ b/src/hooks/common/use-frontend-assets-url.ts @@ -0,0 +1,12 @@ +/* + * SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file) + * + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { useFrontendBaseUrl } from './use-frontend-base-url' + +export const useFrontendAssetsUrl = (): string => { + const frontendBaseUrl = useFrontendBaseUrl() + return (process.env.REACT_APP_FRONTEND_ASSETS_URL || frontendBaseUrl) +} diff --git a/src/index.tsx b/src/index.tsx index 91812b9ab..8a8fbe663 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -27,10 +27,11 @@ import { isTestMode } from './utils/test-modes' const EditorPage = React.lazy(() => import(/* webpackPrefetch: true *//* webpackChunkName: "editor" */ './components/editor-page/editor-page')) const RenderPage = React.lazy(() => import (/* webpackPrefetch: true *//* webpackChunkName: "renderPage" */ './components/render-page/render-page')) const DocumentReadOnlyPage = React.lazy(() => import (/* webpackPrefetch: true *//* webpackChunkName: "documentReadOnly" */ './components/document-read-only-page/document-read-only-page')) +const baseUrl = new URL(document.head.baseURI).pathname ReactDOM.render( - + @@ -93,4 +94,3 @@ if (isTestMode()) { // unregister() to register() below. Note this comes with some pitfalls. // Learn more about service workers: https://bit.ly/CRA-PWA serviceWorkerRegistration.unregister() - diff --git a/src/utils/test-modes.ts b/src/utils/test-modes.ts index 5acf215f8..07570b268 100644 --- a/src/utils/test-modes.ts +++ b/src/utils/test-modes.ts @@ -9,5 +9,5 @@ export const isTestMode = (): boolean => { } export const isMockMode = (): boolean => { - return process.env.REACT_APP_BACKEND === undefined + return process.env.REACT_APP_BACKEND_BASE_URL === undefined }