diff --git a/.gitignore b/.gitignore index 927820040..2bf8ae079 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,8 @@ # compiled output /dist /tmp +/.tmp +/src/.tmp /out-tsc # dependencies diff --git a/.travis.yml b/.travis.yml index 57f513972..123d7e5d2 100644 --- a/.travis.yml +++ b/.travis.yml @@ -44,6 +44,16 @@ env: - ADMIN_EMAIL=$ADMIN_EMAIL_REMOTE - ADMIN_PASSWORD=$ADMIN_PASSWORD_REMOTE - CONTENT_CE_DIST_PATH="./dist/content-ce" + # APP CONFIG DEFAULTS + - APP_CONFIG_PROVIDER=ECM + - APP_CONFIG_AUTH_TYPE=BASIC + - APP_CONFIG_OAUTH2_HOST=http://localhost:4200/auth/realms/alfresco + - APP_CONFIG_OAUTH2_CLIENTID=alfresco + - APP_CONFIG_OAUTH2_IMPLICIT_FLOW=true + - APP_CONFIG_OAUTH2_SILENT_LOGIN=true + - APP_CONFIG_OAUTH2_REDIRECT_LOGOUT=/ + - APP_CONFIG_OAUTH2_REDIRECT_LOGIN=/ + - APP_CONFIG_OAUTH2_REDIRECT_SILENT_IFRAME_URI="{protocol}//{hostname}{:port}/assets/silent-refresh.html" jobs: include: @@ -54,7 +64,7 @@ jobs: - stage: Quality and Unit tests name: 'Build (without animation)' before_script: npx @alfresco/adf-cli update-commit-sha --pointer "HEAD" --pathPackage "$(pwd)" - script: npm ci && npm run build.e2e + script: npm ci && npm run build -- --prod --configuration=e2e after_success: ./scripts/ci/utils/artifact-to-s3.sh -a $CONTENT_CE_DIST_PATH -o "$S3_DBP_FOLDER/alfresco-content-app.tar.bz2" || travis_terminate 1 cache: false diff --git a/angular.json b/angular.json index 915f98685..ef879c26a 100644 --- a/angular.json +++ b/angular.json @@ -39,7 +39,11 @@ }, "src/assets", "src/favicon-96x96.png", - "src/app.config.json", + { + "input": "src/.tmp", + "output": "/", + "glob": "app.config.json" + }, { "glob": "**/*", "input": "node_modules/@alfresco/adf-core/prebuilt-themes", @@ -236,7 +240,11 @@ "assets": [ "src/assets", "src/favicon-96x96.png", - "src/app.config.json", + { + "input": "src/.tmp", + "output": "/", + "glob": "app.config.json" + }, { "glob": "**/*", "input": "node_modules/@alfresco/adf-core/prebuilt-themes", diff --git a/docs/getting-started/building-from-source.md b/docs/getting-started/building-from-source.md index 5d7b1b273..3845ead66 100644 --- a/docs/getting-started/building-from-source.md +++ b/docs/getting-started/building-from-source.md @@ -29,11 +29,20 @@ The application runs at port `4200` by default, and should automatically open in ## Setting up environment variables -You might need to set some environment variables to be able to run the local dev server. In the project's root folder, create a `.env` file (this is gitignored) with the following data: +We need to set some environment variable to be able to run the local dev server. In the project root folder, create an `.env` file (this is gitignored) with the following data: ```bash -API_CONTENT_HOST="http://your-url-here" -``` +# App config settings +APP_CONFIG_ECM_HOST="" +APP_CONFIG_OAUTH2_HOST="" +APP_CONFIG_PROVIDER="BPM" +APP_CONFIG_AUTH_TYPE="BASIC" +APP_CONFIG_OAUTH2_CLIENTID="clientid" +APP_CONFIG_OAUTH2_IMPLICIT_FLOW=true +APP_CONFIG_OAUTH2_SILENT_LOGIN=true +APP_CONFIG_OAUTH2_REDIRECT_SILENT_IFRAME_URI="{protocol}//{hostname}{:port}/assets/silent-refresh.html" +APP_CONFIG_OAUTH2_REDIRECT_LOGIN=/ +APP_CONFIG_OAUTH2_REDIRECT_LOGOUT=/ ## Proxy settings diff --git a/e2e/lite-server-proxy.js b/e2e/lite-server-proxy.js index 8e225381b..d040a6236 100644 --- a/e2e/lite-server-proxy.js +++ b/e2e/lite-server-proxy.js @@ -1,5 +1,43 @@ let fallback = require('connect-history-api-fallback'); +require('@alfresco/adf-cli/tooling').dotenvConfig(); +var proxyMiddleware = require('http-proxy-middleware'); +function getProxy(contentHost) { + return { + '/alfresco': { + target: contentHost, + secure: false, + changeOrigin: true, + // workaround for REPO-2260 + onProxyRes: function (proxyRes, req, res) { + const header = proxyRes.headers['www-authenticate']; + if (header && header.startsWith('Basic')) { + proxyRes.headers['www-authenticate'] = 'x' + header; + } + }, + } + } +} + +function getMiddleWares() { + var counter = 0; + const proxies = { + ...getProxy(process.env.APP_CONFIG_ECM_HOST), + }; + const appMiddlewares = Object.keys(proxies).reduce((accumulator, proxyName) => { + counter++; + return { + ...accumulator, + [counter]: proxyMiddleware(proxyName, proxies[proxyName]), + } + }, {}); + counter++; + + return { + ...appMiddlewares, + [counter]: fallback({ index: '/index.html', verbose: true }), + }; +} module.exports = { injectChanges: false, // workaround for Angular 2 styleUrls loading files: ['./**/*.{html,htm,css,js}'], @@ -11,8 +49,6 @@ module.exports = { port: 4200, open: false, server: { - middleware: { - 1: fallback({ index: '/index.html', verbose: true }) - } + middleware: getMiddleWares(), } -}; +}; \ No newline at end of file diff --git a/license-header.txt b/license-header-aca.txt similarity index 100% rename from license-header.txt rename to license-header-aca.txt diff --git a/package-lock.json b/package-lock.json index f0ae7ad51..5cb9ab177 100644 --- a/package-lock.json +++ b/package-lock.json @@ -6563,6 +6563,12 @@ "integrity": "sha512-8sJ78ElpbDJBHNeBzUbUVLsqKdccaa/BXF1uPTw3GrvQTBgrQrtObr2mUrE38vzYd8cEv+m/JBfDLioYcfXoaw==", "dev": true }, + "dotenv-expand": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/dotenv-expand/-/dotenv-expand-5.1.0.tgz", + "integrity": "sha512-YXQl1DSa4/PQyRfgrv6aoNjhasp/p4qs9FjJ4q4cQk+8m4r6k4ZSiEyytKG8f8W9gi8WsQtIObNmKd+tMzNTmA==", + "dev": true + }, "duplexify": { "version": "3.7.1", "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.7.1.tgz", @@ -6832,6 +6838,78 @@ "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==", "dev": true }, + "envsub": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/envsub/-/envsub-4.0.7.tgz", + "integrity": "sha512-Vr2qsbaTeJEstIoT0GlKnoySmvL91b8LI3D/aicuo1styWcNUvfYv3oeFD2lhB1+S6H/e2TvEkZbmhzDM73Okw==", + "dev": true, + "requires": { + "bluebird": "^3.7.2", + "chalk": "^3.0.0", + "commander": "^4.0.1", + "diff": "^4.0.1", + "handlebars": "^4.5.3", + "lodash": "^4.17.15", + "replace-last": "^1.2.6" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", + "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "commander": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", + "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, "err-code": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/err-code/-/err-code-1.1.2.tgz", @@ -8061,6 +8139,27 @@ "integrity": "sha512-9Qn4yBxelxoh2Ow62nP+Ka/kMnOXRi8BXnRaUwezLNhqelnN49xKz4F/dPP8OYLxLxq6JDtZb2i9XznUQbNPTg==", "dev": true }, + "handlebars": { + "version": "4.7.7", + "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.7.tgz", + "integrity": "sha512-aAcXm5OAfE/8IXkcZvCepKU3VzW1/39Fb5ZuqMtgI/hT8X2YgoMvBY5dLhq/cpOvw7Lk1nK/UF71aLG/ZnVYRA==", + "dev": true, + "requires": { + "minimist": "^1.2.5", + "neo-async": "^2.6.0", + "source-map": "^0.6.1", + "uglify-js": "^3.1.4", + "wordwrap": "^1.0.0" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, "har-schema": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", @@ -13973,6 +14072,12 @@ "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=", "dev": true }, + "replace-last": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/replace-last/-/replace-last-1.2.6.tgz", + "integrity": "sha512-Cj+MK38VtNu1S5J73mEZY3ciQb9dJajNq1Q8inP4dn/MhJMjHwoAF3Z3FjspwAEV9pfABl565MQucmrjOkty4g==", + "dev": true + }, "request": { "version": "2.88.2", "resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz", @@ -16793,6 +16898,13 @@ "integrity": "sha512-qLK/Xe9E2uzmYI3qLeOmI0tEOt+TBBQyUIAh4aAgU05FVYzeZrKUdkAZfBNVGRaHVgV0TDkdEngJSw/SyQchkQ==", "dev": true }, + "uglify-js": { + "version": "3.14.4", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.14.4.tgz", + "integrity": "sha512-AbiSR44J0GoCeV81+oxcy/jDOElO2Bx3d0MfQCUShq7JRXaM4KtQopZsq2vFv8bCq2yMaGrw1FgygUd03RyRDA==", + "dev": true, + "optional": true + }, "unbox-primitive": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.1.tgz", @@ -18580,6 +18692,12 @@ } } }, + "wordwrap": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", + "integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=", + "dev": true + }, "worker-farm": { "version": "1.7.0", "resolved": "https://registry.npmjs.org/worker-farm/-/worker-farm-1.7.0.tgz", diff --git a/package.json b/package.json index 84a2f2069..a09103ee9 100644 --- a/package.json +++ b/package.json @@ -6,21 +6,23 @@ "scripts": { "postinstall": "ngcc", "ng": "ng", - "start": "npm run validate-config && ng serve --open", - "start:prod": "npm run validate-config && node --max-old-space-size=8192 node_modules/@angular/cli/bin/ng serve --prod --open", + "validate-app-config": "ajv validate -s ./node_modules/@alfresco/adf-core/app.config.schema.json -d ./src/.tmp/app.config.json --errors=text --verbose", + "assemble-app-config": "envsub --env-file ./.env --env APP_CONFIG_ECM_HOST={protocol}//{hostname}{:port} --env APP_CONFIG_PROVIDER=ECM --env APP_CONFIG_AUTH_TYPE=BASIC --env APP_CONFIG_OAUTH2_HOST=http://localhost:4200/auth/realms/alfresco --env APP_CONFIG_OAUTH2_CLIENTID=alfresco --env APP_CONFIG_OAUTH2_IMPLICIT_FLOW=true --env APP_CONFIG_OAUTH2_SILENT_LOGIN=true --env APP_CONFIG_OAUTH2_REDIRECT_SILENT_IFRAME_URI={protocol}//{hostname}{:port}/assets/silent-refresh.html --env APP_CONFIG_OAUTH2_REDIRECT_LOGIN=/ --env APP_CONFIG_OAUTH2_REDIRECT_LOGOUT=/logout --all ./src/app.config.json.tpl ./src/.tmp/app.config.json", + "prestart": "mkdir -p ./src/.tmp && npm run assemble-app-config && npm run validate-app-config", + "start": "ng serve", + "start:prod": "npm run validate-app-config && node --max-old-space-size=8192 node_modules/@angular/cli/bin/ng serve --prod --open", "build.app": "node --max-old-space-size=8192 node_modules/@angular/cli/bin/ng build app", - "build": "npm run validate-config && npm run build.app -- --prod", - "build.e2e": "npm run build.app -- --prod --configuration=e2e", + "prebuild": "mkdir -p ./src/.tmp && cp ./src/app.config.json.tpl ./src/.tmp/app.config.json", + "build": "ng build", "build.release": "npm run build.app -- --configuration=production,release", - "test": "ng test app --code-coverage", + "test": "ng test", "test:ci": "ng test adf-office-services-ext --watch=false && ng test app --code-coverage --watch=false", "lint": "ng lint && npm run spellcheck && npm run e2e.typecheck", "update-webdriver": "./scripts/update-webdriver.sh", "e2e.typecheck": "tsc -p ./e2e/tsconfig.e2e.typecheck.json", "e2e": "npm run update-webdriver && protractor $SUITE", "spellcheck": "cspell '{src,e2e,projects}/**/*.ts'", - "inspect.bundle": "ng build app --prod --stats-json && npx webpack-bundle-analyzer dist/content-ce/stats.json", - "validate-config": "ajv validate -s ./node_modules/@alfresco/adf-core/app.config.schema.json -d ./src/app.config.json --errors=text --verbose" + "inspect.bundle": "ng build app --prod --stats-json && npx webpack-bundle-analyzer dist/app/stats.json" }, "private": true, "dependencies": { @@ -79,6 +81,8 @@ "connect-history-api-fallback": "^1.6.0", "cspell": "^5.13.1", "dotenv": "^8.2.0", + "dotenv-expand": "^5.1.0", + "envsub": "^4.0.7", "http-server": "^14.0.0", "husky": "^7.0.2", "inquirer": "^8.1.5", diff --git a/scripts/ci/job_hooks/before_e2e.sh b/scripts/ci/job_hooks/before_e2e.sh index ae8b05491..ed0be4145 100755 --- a/scripts/ci/job_hooks/before_e2e.sh +++ b/scripts/ci/job_hooks/before_e2e.sh @@ -1,5 +1,20 @@ #!/usr/bin/env bash +# --------------------------------------------------------------- +# Validating replaced app.config.json +# --------------------------------------------------------------- +app_config_checker(){ + APP_CONFIG_FILE_PATH=$1; + + echo -n " \_ Validating replaced config file ... "; + $(npm bin)/ajv validate -s ./node_modules/@alfresco/adf-core/app.config.schema.json -d $APP_CONFIG_FILE_PATH --errors=text --verbose || exit 4 + + if grep -E -q '\$\{[A-Z0-9_]*\}' $APP_CONFIG_FILE_PATH; then + echo -e "\e[31m \_ ERROR: Variables are still present in the app.config.json file. Some of them might not have default value set.\e[0m"; + exit 5; + fi +} + FROM=$1; TO=$2; PARAMS=$3; @@ -11,7 +26,13 @@ echo "====== Check content UP =====" echo "====== Download artifacts =====" # Download built application artifact from S3 ./scripts/ci/utils/artifact-from-s3.sh -a "$FROM" -o "$TO" -node "./scripts/app-config-replace.js" --config="$TO/app.config.json" $PARAMS + +APP_CONFIG_FILE_PATH="$TO/app.config.json" +EXTRA_ENV_SETTINGS="" +# Replace variables in app.config.json +envsub $EXTRA_ENV_SETTINGS --all $APP_CONFIG_FILE_PATH $APP_CONFIG_FILE_PATH || exit 1 + +app_config_checker $APP_CONFIG_FILE_PATH # Download protractor-smartrunner artifact related to this particular job from S3, if exists ./scripts/ci/utils/artifact-from-s3.sh -a "$S3_DBP_FOLDER/protractor-smartrunner-$TRAVIS_JOB_ID.tar.bz2" -o "$SMART_RUNNER_DIRECTORY" diff --git a/src/app.config.json b/src/app.config.json.tpl similarity index 93% rename from src/app.config.json rename to src/app.config.json.tpl index 4c2ee97bb..3d6e48dff 100644 --- a/src/app.config.json +++ b/src/app.config.json.tpl @@ -1,22 +1,22 @@ { "$schema": "../node_modules/@alfresco/adf-core/app.config.schema.json", - "ecmHost": "{protocol}//{hostname}{:port}", - "aosHost": "{protocol}//{hostname}{:port}/alfresco/aos", - "baseShareUrl": "{protocol}//{hostname}{:port}/#/preview/s", - "providers": "ECM", - "authType": "BASIC", + "ecmHost": "${APP_CONFIG_ECM_HOST}", + "aosHost": "${APP_CONFIG_ECM_HOST}/alfresco/aos", + "baseShareUrl": "${APP_CONFIG_ECM_HOST}/#/preview/s", + "providers": "${APP_CONFIG_PROVIDER}", + "authType": "${APP_CONFIG_AUTH_TYPE}", "loginRoute": "login", "oauth2": { - "host": "http://localhost:4200/auth/realms/alfresco", - "clientId": "alfresco", + "host": "${APP_CONFIG_OAUTH2_HOST}", + "clientId": "${APP_CONFIG_OAUTH2_CLIENTID}", "scope": "openid", "secret": "", - "implicitFlow": true, - "silentLogin": true, + "implicitFlow": ${APP_CONFIG_OAUTH2_IMPLICIT_FLOW}, + "silentLogin": ${APP_CONFIG_OAUTH2_SILENT_LOGIN}, "publicUrls": ["**/preview/s/*", "**/settings", "**/blank"], - "redirectSilentIframeUri": "{protocol}//{hostname}{:port}/assets/silent-refresh.html", - "redirectUri": "/", - "redirectUriLogout": "/logout" + "redirectSilentIframeUri": "${APP_CONFIG_OAUTH2_REDIRECT_SILENT_IFRAME_URI}", + "redirectUri": "${APP_CONFIG_OAUTH2_REDIRECT_LOGIN}", + "redirectUriLogout": "${APP_CONFIG_OAUTH2_REDIRECT_LOGOUT}" }, "locale": "en", "application": { diff --git a/src/proxy.conf.js b/src/proxy.conf.js index 0b4320752..3bc754ac6 100644 --- a/src/proxy.conf.js +++ b/src/proxy.conf.js @@ -1,23 +1,15 @@ -require('dotenv').config(); +require('@alfresco/adf-cli/tooling').dotenvConfig({ path: process.env.ENV_FILE }); -const APP_CONFIG_ECM_HOST = process.env.APP_CONFIG_ECM_HOST || 'http://0.0.0.0:8080'; +const APP_CONFIG_ECM_HOST = process.env.APP_CONFIG_ECM_HOST; module.exports = { - '/alfresco': { - target: APP_CONFIG_ECM_HOST, - secure: false, - changeOrigin: true, - // workaround for REPO-2260 - onProxyRes: function (proxyRes) { - const header = proxyRes.headers['www-authenticate']; - if (header && header.startsWith('Basic')) { - proxyRes.headers['www-authenticate'] = 'x' + header; - } + "/alfresco": { + "target": APP_CONFIG_ECM_HOST, + "secure": false, + "pathRewrite": { + "^/alfresco/alfresco": "" + }, + "changeOrigin": true, + 'logLevel': 'debug' } - }, - '/auth': { - target: APP_CONFIG_ECM_HOST, - secure: false, - changeOrigin: true - } }; diff --git a/tslint.json b/tslint.json index 70be2e6e7..42cb98681 100644 --- a/tslint.json +++ b/tslint.json @@ -11,7 +11,7 @@ ], "rules": { "prettier": true, - "adf-license-banner": [true, "**/*.ts", "./license-header.txt"], + "adf-license-banner": [true, "**/*.ts", "./license-header-aca.txt"], "ban": [ true, "eval",