initial implementation
This commit is contained in:
		
							
								
								
									
										7
									
								
								.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,7 @@ | ||||
| .nogit/ | ||||
| node_modules/ | ||||
| assets/pdfdir/ | ||||
| coverage/ | ||||
| public/ | ||||
| pages/ | ||||
| .yarn/ | ||||
							
								
								
									
										147
									
								
								.gitlab-ci.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										147
									
								
								.gitlab-ci.yml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,147 @@ | ||||
| # gitzone standard | ||||
| image: hosttoday/ht-docker-node:npmci | ||||
|  | ||||
| cache: | ||||
|   paths: | ||||
|   - .npmci_cache/ | ||||
|   key: "$CI_BUILD_STAGE" | ||||
|  | ||||
| stages: | ||||
| - security | ||||
| - test | ||||
| - release | ||||
| - metadata | ||||
|  | ||||
| # ==================== | ||||
| # security stage | ||||
| # ==================== | ||||
| mirror: | ||||
|   stage: security | ||||
|   script: | ||||
|   - npmci git mirror | ||||
|   tags: | ||||
|   - docker | ||||
|   - notpriv | ||||
|  | ||||
| snyk: | ||||
|   stage: security | ||||
|   script: | ||||
|     - npmci npm prepare | ||||
|     - npmci command npm install -g snyk | ||||
|     - npmci command npm install --ignore-scripts | ||||
|     - npmci command snyk test | ||||
|   tags: | ||||
|   - docker | ||||
|   - notpriv | ||||
|  | ||||
| # ==================== | ||||
| # test stage | ||||
| # ==================== | ||||
| testLEGACY: | ||||
|   stage: test | ||||
|   script: | ||||
|   - npmci npm prepare | ||||
|   - npmci node install legacy | ||||
|   - npmci npm install | ||||
|   - npmci npm test | ||||
|   coverage: /\d+.?\d+?\%\s*coverage/ | ||||
|   tags: | ||||
|   - docker | ||||
|   - notpriv | ||||
|   allow_failure: true | ||||
|  | ||||
| testLTS: | ||||
|   stage: test | ||||
|   script: | ||||
|   - npmci npm prepare | ||||
|   - npmci node install lts | ||||
|   - npmci npm install | ||||
|   - npmci npm test | ||||
|   coverage: /\d+.?\d+?\%\s*coverage/ | ||||
|   tags: | ||||
|   - docker | ||||
|   - notpriv | ||||
|      | ||||
| testSTABLE: | ||||
|   stage: test | ||||
|   script: | ||||
|   - npmci npm prepare | ||||
|   - npmci node install stable | ||||
|   - npmci npm install | ||||
|   - npmci npm test | ||||
|   coverage: /\d+.?\d+?\%\s*coverage/ | ||||
|   tags: | ||||
|   - docker | ||||
|   - notpriv | ||||
|  | ||||
| release: | ||||
|   stage: release | ||||
|   script: | ||||
|   - npmci node install stable | ||||
|   - npmci npm publish | ||||
|   only: | ||||
|   - tags | ||||
|   tags: | ||||
|   - docker | ||||
|   - notpriv | ||||
|  | ||||
| # ==================== | ||||
| # metadata stage | ||||
| # ==================== | ||||
| codequality: | ||||
|   stage: metadata | ||||
|   image: docker:stable | ||||
|   allow_failure: true | ||||
|   services: | ||||
|     - docker:stable-dind | ||||
|   script: | ||||
|     - export SP_VERSION=$(echo "$CI_SERVER_VERSION" | sed 's/^\([0-9]*\)\.\([0-9]*\).*/\1-\2-stable/') | ||||
|     - docker run | ||||
|         --env SOURCE_CODE="$PWD" | ||||
|         --volume "$PWD":/code | ||||
|         --volume /var/run/docker.sock:/var/run/docker.sock | ||||
|         "registry.gitlab.com/gitlab-org/security-products/codequality:$SP_VERSION" /code | ||||
|   artifacts: | ||||
|     paths: [codeclimate.json] | ||||
|   tags: | ||||
|   - docker | ||||
|   - priv | ||||
|  | ||||
| trigger: | ||||
|   stage: metadata | ||||
|   script: | ||||
|   - npmci trigger | ||||
|   only: | ||||
|   - tags | ||||
|   tags: | ||||
|   - docker | ||||
|   - notpriv | ||||
|  | ||||
| pages: | ||||
|   image: hosttoday/ht-docker-node:npmci | ||||
|   stage: metadata | ||||
|   script: | ||||
|     - npmci command npm install -g typedoc typescript | ||||
|     - npmci npm prepare | ||||
|     - npmci npm install | ||||
|     - npmci command typedoc --module "commonjs" --target "ES2016" --out public/ ts/ | ||||
|   tags: | ||||
|     - docker | ||||
|     - notpriv | ||||
|   only: | ||||
|     - tags | ||||
|   artifacts: | ||||
|     expire_in: 1 week | ||||
|     paths: | ||||
|     - public | ||||
|   allow_failure: true | ||||
|  | ||||
| windowsCompatibility: | ||||
|   image: stefanscherer/node-windows:10-build-tools | ||||
|   stage: metadata | ||||
|   script: | ||||
|   - npm install & npm test | ||||
|   coverage: /\d+.?\d+?\%\s*coverage/ | ||||
|   tags: | ||||
|   - windows | ||||
|   allow_failure: true | ||||
							
								
								
									
										8
									
								
								npmextra.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								npmextra.json
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,8 @@ | ||||
| { | ||||
|   "npmci": { | ||||
|     "npmGlobalTools": [ | ||||
|       "@gitzone/npmts", | ||||
|       "ts-node" | ||||
|     ] | ||||
|   } | ||||
| } | ||||
							
								
								
									
										1578
									
								
								package-lock.json
									
									
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						
									
										1578
									
								
								package-lock.json
									
									
									
										generated
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										30
									
								
								package.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								package.json
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,30 @@ | ||||
| { | ||||
|   "name": "smartpdf", | ||||
|   "version": "1.0.1", | ||||
|   "description": "create pdfs on the fly", | ||||
|   "main": "dist/index.js", | ||||
|   "typings": "dist/index.d.ts", | ||||
|   "author": "Lossless GmbH", | ||||
|   "license": "MIT", | ||||
|   "scripts": { | ||||
|     "test": "tstest test/", | ||||
|     "format": "(gitzone format)" | ||||
|   }, | ||||
|   "devDependencies": { | ||||
|     "@gitzone/tsbuild": "^2.0.22", | ||||
|     "@gitzone/tsrun": "^1.1.12", | ||||
|     "@gitzone/tstest": "^1.0.15", | ||||
|     "@pushrocks/tapbundle": "^3.0.7", | ||||
|     "@types/node": "^10.11.4" | ||||
|   }, | ||||
|   "dependencies": { | ||||
|     "@pushrocks/smartfile": "^6.0.8", | ||||
|     "@pushrocks/smartnetwork": "^1.1.0", | ||||
|     "@pushrocks/smartpromise": "^2.0.5", | ||||
|     "@types/express": "^4.16.0", | ||||
|     "@types/puppeteer": "^1.8.0", | ||||
|     "express": "^4.16.3", | ||||
|     "puppeteer": "^1.8.0", | ||||
|     "smartunique": "^2.0.0" | ||||
|   } | ||||
| } | ||||
							
								
								
									
										19
									
								
								test/test.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								test/test.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,19 @@ | ||||
| import { expect, tap } from '@pushrocks/tapbundle'; | ||||
| import * as smartpdf from '../ts/index' | ||||
|  | ||||
| let testSmartPdf: smartpdf.SmartPdf; | ||||
|  | ||||
| tap.test('should create a valid instance of smartpdf', async () => { | ||||
|   testSmartPdf = new smartpdf.SmartPdf(); | ||||
|   expect(testSmartPdf).to.be.instanceof(smartpdf.SmartPdf); | ||||
| }) | ||||
|  | ||||
| tap.test('should create a pdf from html string', async () => { | ||||
|   await testSmartPdf.getPdfForHtmlString('hi'); | ||||
| }); | ||||
|  | ||||
| tap.test('should create a pdf from website', async () => { | ||||
|   await testSmartPdf.getPdfForWebsite('https://wikipedia.org'); | ||||
| }); | ||||
|  | ||||
| tap.start() | ||||
							
								
								
									
										1
									
								
								ts/index.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								ts/index.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1 @@ | ||||
| export * from './smartpdf.classes.smartpdf'; | ||||
							
								
								
									
										8
									
								
								ts/smartpdf.classes.pdfcandidate.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								ts/smartpdf.classes.pdfcandidate.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,8 @@ | ||||
| import * as plugins from './smartpdf.plugins'; | ||||
|  | ||||
| export class PdfCandidate { | ||||
|   pdfId = plugins.smartunique.shortId(); | ||||
|   doneDeferred = plugins.smartpromise.defer(); | ||||
|  | ||||
|   constructor(public htmlString) {} | ||||
| } | ||||
							
								
								
									
										1
									
								
								ts/smartpdf.classes.pdfresult.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								ts/smartpdf.classes.pdfresult.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1 @@ | ||||
| import * as plugins from './smartpdf.plugins'; | ||||
							
								
								
									
										74
									
								
								ts/smartpdf.classes.smartpdf.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										74
									
								
								ts/smartpdf.classes.smartpdf.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,74 @@ | ||||
| import * as plugins from './smartpdf.plugins'; | ||||
| import * as paths from './smartpdf.paths'; | ||||
| import { Server } from 'http'; | ||||
| import { PdfCandidate } from './smartpdf.classes.pdfcandidate'; | ||||
|  | ||||
| export class SmartPdf { | ||||
|   htmlServerInstance: Server; | ||||
|   serverPort: number; | ||||
|   headlessBrowser: plugins.puppeteer.Browser; | ||||
|   private _readyDeferred: plugins.smartpromise.Deferred<void>; | ||||
|   private _candidates: {[key: string]: PdfCandidate} = {}; | ||||
|  | ||||
|   constructor() { | ||||
|     this._readyDeferred = new plugins.smartpromise.Deferred(); | ||||
|     this.init(); | ||||
|   } | ||||
|  | ||||
|   async init() { | ||||
|     // setup puppeteer | ||||
|     this.headlessBrowser = await plugins.puppeteer.launch(); | ||||
|  | ||||
|     // setup server | ||||
|     const app = plugins.express(); | ||||
|     app.get('/:pdfId', (req, res) => { | ||||
|       res.setHeader('PDF-ID', this._candidates[req.params.pdfId].pdfId); | ||||
|       res.send(this._candidates[req.params.pdfId].htmlString); | ||||
|     }); | ||||
|     this.htmlServerInstance = plugins.http.createServer(app); | ||||
|     const smartnetworkInstance = new plugins.smartnetwork.SmartNetwork(); | ||||
|     const portAvailable = smartnetworkInstance.isLocalPortAvailable(3210); | ||||
|     this.htmlServerInstance.listen(3210, 'localhost'); | ||||
|     this.htmlServerInstance.on('listening', () => { | ||||
|       this._readyDeferred.resolve(); | ||||
|     }); | ||||
|   } | ||||
|  | ||||
|   /** | ||||
|    * returns a pdf for a given html string; | ||||
|    */ | ||||
|   async getPdfForHtmlString(htmlStringArg: string) { | ||||
|     await this._readyDeferred.promise; | ||||
|     const pdfCandidate = new PdfCandidate(htmlStringArg); | ||||
|     this._candidates[pdfCandidate.pdfId] = pdfCandidate; | ||||
|     const page = await this.headlessBrowser.newPage(); | ||||
|     const response = await page.goto(`http://localhost:3210/${pdfCandidate.pdfId}`, { waitUntil: 'networkidle2' }); | ||||
|     const headers = response.headers(); | ||||
|     if(headers['pdf-id'] !== pdfCandidate.pdfId) { | ||||
|       console.log('Error! Headers do not match. For security reasons no pdf is being emitted!'); | ||||
|       return | ||||
|     } else { | ||||
|       console.log(`id security check passed for ${pdfCandidate.pdfId}`); | ||||
|     } | ||||
|      | ||||
|     await page.pdf({ | ||||
|       path: plugins.path.join(paths.pdfDir, `${pdfCandidate.pdfId}.pdf`), | ||||
|       format: 'A4' | ||||
|     }); | ||||
|     await page.close(); | ||||
|     delete this._candidates[pdfCandidate.pdfId]; | ||||
|     pdfCandidate.doneDeferred.resolve(); | ||||
|     await pdfCandidate.doneDeferred.promise; | ||||
|   } | ||||
|  | ||||
|   async getPdfForWebsite(websiteUrl: string) { | ||||
|     const page = await this.headlessBrowser.newPage(); | ||||
|     const response = await page.goto(websiteUrl, { waitUntil: 'networkidle2' }); | ||||
|     const pdfId = plugins.smartunique.shortId(); | ||||
|     await page.pdf({ | ||||
|       path: plugins.path.join(paths.pdfDir, `${pdfId}.pdf`), | ||||
|       format: 'A4' | ||||
|     }); | ||||
|     await page.close(); | ||||
|   } | ||||
| } | ||||
							
								
								
									
										6
									
								
								ts/smartpdf.paths.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								ts/smartpdf.paths.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,6 @@ | ||||
| import * as plugins from './smartpdf.plugins'; | ||||
|  | ||||
| export const packageDir = plugins.path.join(__dirname, '../'); | ||||
| export const pdfDir = plugins.path.join(packageDir, 'assets/pdfdir'); | ||||
|  | ||||
| plugins.smartfile.fs.ensureDirSync(pdfDir); | ||||
							
								
								
									
										30
									
								
								ts/smartpdf.plugins.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								ts/smartpdf.plugins.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,30 @@ | ||||
| // native | ||||
| import * as http from 'http'; | ||||
| import * as path from 'path'; | ||||
|  | ||||
| export { | ||||
|   http, | ||||
|   path | ||||
| } | ||||
|  | ||||
| // @pushrocks | ||||
| import * as smartfile from '@pushrocks/smartfile'; | ||||
| import * as smartpromise from '@pushrocks/smartpromise'; | ||||
| import * as smartnetwork from '@pushrocks/smartnetwork'; | ||||
| import * as smartunique from 'smartunique'; | ||||
|  | ||||
| export { | ||||
|   smartfile, | ||||
|   smartpromise, | ||||
|   smartunique, | ||||
|   smartnetwork | ||||
| } | ||||
|  | ||||
| // thirdparty | ||||
| import * as express from 'express'; | ||||
| import * as puppeteer from 'puppeteer'; | ||||
|  | ||||
| export { | ||||
|   express, | ||||
|   puppeteer | ||||
| } | ||||
							
								
								
									
										7
									
								
								tslint.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								tslint.json
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,7 @@ | ||||
| { | ||||
|     "extends": ["tslint:latest", "tslint-config-prettier"], | ||||
|     "rules": { | ||||
|       "semicolon": [true, "always"] | ||||
|     } | ||||
|   } | ||||
|    | ||||
		Reference in New Issue
	
	Block a user