├── server.ts ├── browser.ts ├── src ├── browser │ ├── index.ts │ ├── browser-cookies.module.ts │ └── browser-cookies.service.ts ├── server │ ├── index.ts │ ├── server-cookies.module.ts │ └── server-cookies.service.ts ├── cookies-options.ts ├── cookies-options.service.ts ├── utils.ts └── cookies.service.ts ├── index.ts ├── .editorconfig ├── rollup.config.js ├── .gitignore ├── tsconfig.json ├── LICENSE ├── tslint.json ├── package.json ├── CODE_OF_CONDUCT.md └── README.md /server.ts: -------------------------------------------------------------------------------- 1 | export * from './src/server'; 2 | -------------------------------------------------------------------------------- /browser.ts: -------------------------------------------------------------------------------- 1 | export * from './src/browser'; 2 | -------------------------------------------------------------------------------- /src/browser/index.ts: -------------------------------------------------------------------------------- 1 | export * from './browser-cookies.module'; 2 | export * from './browser-cookies.service'; 3 | -------------------------------------------------------------------------------- /src/server/index.ts: -------------------------------------------------------------------------------- 1 | export * from './server-cookies.module'; 2 | export * from './server-cookies.service'; 3 | -------------------------------------------------------------------------------- /index.ts: -------------------------------------------------------------------------------- 1 | export * from './src/cookies.service'; 2 | export * from './src/cookies-options.service'; 3 | export * from './src/cookies-options'; 4 | -------------------------------------------------------------------------------- /src/cookies-options.ts: -------------------------------------------------------------------------------- 1 | export interface CookiesOptions { 2 | path?: string | null; 3 | domain?: string | null; 4 | expires?: string | Date | null; 5 | secure?: boolean | null; 6 | httpOnly?: boolean | null; 7 | } 8 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # http://editorconfig.org 2 | 3 | root = true 4 | 5 | [*] 6 | charset = utf-8 7 | indent_style = space 8 | indent_size = 2 9 | end_of_line = lf 10 | insert_final_newline = true 11 | trim_trailing_whitespace = true 12 | 13 | [*.md] 14 | insert_final_newline = false 15 | trim_trailing_whitespace = false 16 | -------------------------------------------------------------------------------- /rollup.config.js: -------------------------------------------------------------------------------- 1 | export default { 2 | entry: './release/index.js', 3 | dest: './release/bundles/cookies.umd.js', 4 | sourceMap: false, 5 | format: 'umd', 6 | moduleName: 'ngx-utils.cookies', 7 | globals: { 8 | '@angular/core': 'ng.core', 9 | '@angular/common': 'ng.common', 10 | 'rxjs/Observable': 'Rx' 11 | } 12 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | 5 | # Runtime data 6 | pids 7 | *.pid 8 | *.seed 9 | .DS_Store 10 | 11 | # Compiled binary addons (http://nodejs.org/api/addons.html) 12 | build/Release 13 | 14 | # Dependency directory 15 | # https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git 16 | node_modules 17 | tmp 18 | typings 19 | *.tgz 20 | .idea 21 | spec/ngc/ngfactory/ 22 | spec/ngc/output/ 23 | release 24 | 25 | *.ngsummary.json 26 | *.ngfactory.ts -------------------------------------------------------------------------------- /src/server/server-cookies.module.ts: -------------------------------------------------------------------------------- 1 | import { ModuleWithProviders, NgModule } from "@angular/core"; 2 | import { CookiesOptions } from "../cookies-options"; 3 | import { 4 | CookiesOptionsService, 5 | COOKIES_OPTIONS 6 | } from "../cookies-options.service"; 7 | import { CookiesService } from "../cookies.service"; 8 | import { ServerCookiesService } from "./server-cookies.service"; 9 | 10 | @NgModule() 11 | export class ServerCookiesModule { 12 | static forRoot(options: CookiesOptions = {}): ModuleWithProviders { 13 | return { 14 | ngModule: ServerCookiesModule, 15 | providers: [ 16 | { provide: COOKIES_OPTIONS, useValue: options }, 17 | CookiesOptionsService, 18 | { provide: CookiesService, useClass: ServerCookiesService } 19 | ] 20 | }; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/browser/browser-cookies.module.ts: -------------------------------------------------------------------------------- 1 | import { ModuleWithProviders, NgModule } from "@angular/core"; 2 | import { CookiesOptions } from "../cookies-options"; 3 | import { 4 | CookiesOptionsService, 5 | COOKIES_OPTIONS 6 | } from "../cookies-options.service"; 7 | import { CookiesService } from "../cookies.service"; 8 | import { BrowserCookiesService } from "./browser-cookies.service"; 9 | 10 | @NgModule() 11 | export class BrowserCookiesModule { 12 | static forRoot(options: CookiesOptions = {}): ModuleWithProviders { 13 | return { 14 | ngModule: BrowserCookiesModule, 15 | providers: [ 16 | { provide: COOKIES_OPTIONS, useValue: options }, 17 | CookiesOptionsService, 18 | { provide: CookiesService, useClass: BrowserCookiesService } 19 | ] 20 | }; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/cookies-options.service.ts: -------------------------------------------------------------------------------- 1 | import { Inject, Injectable, InjectionToken, Injector } from '@angular/core'; 2 | import { APP_BASE_HREF } from '@angular/common'; 3 | 4 | import { CookiesOptions } from './cookies-options'; 5 | import { mergeOptions } from './utils'; 6 | 7 | export const COOKIES_OPTIONS = new InjectionToken('COOKIES_OPTIONS'); 8 | 9 | @Injectable() 10 | export class CookiesOptionsService { 11 | private defaultOptions: CookiesOptions; 12 | private _options: CookiesOptions; 13 | 14 | constructor(@Inject(COOKIES_OPTIONS) options: CookiesOptions = {}, 15 | private injector: Injector) { 16 | this.defaultOptions = { 17 | path: this.injector.get(APP_BASE_HREF, '/'), 18 | domain: null, 19 | expires: null, 20 | secure: false 21 | }; 22 | this._options = mergeOptions(this.defaultOptions, options); 23 | } 24 | 25 | get options(): CookiesOptions { 26 | return this._options; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "baseUrl": ".", 4 | "declaration": true, 5 | "stripInternal": true, 6 | "experimentalDecorators": true, 7 | "strictNullChecks": true, 8 | "noImplicitAny": true, 9 | "module": "es2015", 10 | "moduleResolution": "node", 11 | "paths": { 12 | "@angular/core": [ 13 | "node_modules/@angular/core" 14 | ], 15 | "@angular/common": [ 16 | "node_modules/@angular/common" 17 | ], 18 | "rxjs/*": [ 19 | "node_modules/rxjs/*" 20 | ] 21 | }, 22 | "rootDir": ".", 23 | "outDir": "./release", 24 | "sourceMap": true, 25 | "inlineSources": true, 26 | "target": "es5", 27 | "skipLibCheck": true, 28 | "lib": [ 29 | "es2015", 30 | "dom" 31 | ] 32 | }, 33 | "files": [ 34 | "index.ts", 35 | "browser.ts", 36 | "server.ts" 37 | ], 38 | "angularCompilerOptions": { 39 | "strictMetadataEmit": true 40 | } 41 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2017 Anton Barada 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "rules": { 3 | "class-name": true, 4 | "eofline": true, 5 | "comment-format": [true, "check-space"], 6 | "indent": [true, "spaces"], 7 | "max-line-length": [true, 120], 8 | "member-ordering": [true, {"order": "fields-first"}], 9 | "no-arg": true, 10 | "no-construct": true, 11 | "no-duplicate-variable": true, 12 | "no-eval": true, 13 | "no-internal-module": true, 14 | "no-trailing-whitespace": true, 15 | "one-line": [ 16 | true, 17 | "check-open-brace", 18 | "check-catch", 19 | "check-else", 20 | "check-whitespace" 21 | ], 22 | "quotemark": [true, "single"], 23 | "semicolon": true, 24 | "triple-equals": [true, "allow-null-check"], 25 | "typedef-whitespace": [ 26 | true, 27 | { 28 | "call-signature": "nospace", 29 | "index-signature": "nospace", 30 | "parameter": "nospace", 31 | "property-declaration": "nospace", 32 | "variable-declaration": "nospace" 33 | } 34 | ], 35 | "variable-name": [ 36 | true, 37 | "ban-keywords", 38 | "allow-leading-underscore" 39 | ], 40 | "whitespace": [ 41 | true, 42 | "check-branch", 43 | "check-decl", 44 | "check-operator", 45 | "check-separator", 46 | "check-type" 47 | ] 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/utils.ts: -------------------------------------------------------------------------------- 1 | import { CookiesOptions } from './cookies-options'; 2 | 3 | export function isBlank(obj: any): boolean { 4 | return obj === undefined || obj === null; 5 | } 6 | 7 | export function isPresent(obj: any): boolean { 8 | return obj !== undefined && obj !== null; 9 | } 10 | 11 | export function isString(obj: any): obj is string { 12 | return typeof obj === 'string'; 13 | } 14 | 15 | export function mergeOptions(oldOptions: CookiesOptions, newOptions?: CookiesOptions): CookiesOptions { 16 | if (!newOptions) { 17 | return oldOptions; 18 | } 19 | return { 20 | path: isPresent(newOptions.path) ? newOptions.path : oldOptions.path, 21 | domain: isPresent(newOptions.domain) ? newOptions.domain : oldOptions.domain, 22 | expires: isPresent(newOptions.expires) ? newOptions.expires : oldOptions.expires, 23 | secure: isPresent(newOptions.secure) ? newOptions.secure : oldOptions.secure, 24 | httpOnly: isPresent(newOptions.httpOnly) ? newOptions.httpOnly : oldOptions.httpOnly 25 | }; 26 | } 27 | 28 | export function safeDecodeURIComponent(str: string): string { 29 | try { 30 | return decodeURIComponent(str); 31 | } catch (e) { 32 | return str; 33 | } 34 | } 35 | 36 | export function safeJsonParse(str: string): { [key: string]: any } | string { 37 | try { 38 | return JSON.parse(str); 39 | } catch (e) { 40 | return str; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/cookies.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | 3 | import { CookiesOptions } from './cookies-options'; 4 | import { CookiesOptionsService } from './cookies-options.service'; 5 | import { safeJsonParse } from './utils'; 6 | 7 | @Injectable() 8 | export class CookiesService { 9 | protected options: CookiesOptions; 10 | 11 | constructor(cookiesOptions: CookiesOptionsService) { 12 | this.options = cookiesOptions.options; 13 | } 14 | 15 | put(key: string, value: string, options?: CookiesOptions): void { 16 | this.cookiesWriter()(key, value, options); 17 | } 18 | 19 | putObject(key: string, value: Object, options?: CookiesOptions): void { 20 | this.put(key, JSON.stringify(value), options); 21 | } 22 | 23 | get(key: string): string { 24 | return (this.cookiesReader())[key]; 25 | } 26 | 27 | getObject(key: string): { [key: string]: string } | string { 28 | const value = this.get(key); 29 | return value ? safeJsonParse(value) : value; 30 | } 31 | 32 | getAll(): { [key: string]: string } { 33 | return this.cookiesReader(); 34 | } 35 | 36 | remove(key: string, options?: CookiesOptions): void { 37 | this.cookiesWriter()(key, undefined, options); 38 | } 39 | 40 | removeAll(): void { 41 | const cookies = this.getAll(); 42 | Object.keys(cookies).forEach(key => { 43 | this.remove(key); 44 | }); 45 | } 46 | 47 | protected cookiesReader(): { [key: string]: any } { 48 | return { }; 49 | } 50 | 51 | protected cookiesWriter(): (name: string, value: string | undefined, options?: CookiesOptions) => void { 52 | return () => { }; 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/server/server-cookies.service.ts: -------------------------------------------------------------------------------- 1 | import { Inject, Injectable } from '@angular/core'; 2 | import { CookiesOptions } from '../cookies-options'; 3 | import { CookiesOptionsService } from '../cookies-options.service'; 4 | import { CookiesService } from '../cookies.service'; 5 | import { isString, mergeOptions } from '../utils'; 6 | 7 | @Injectable() 8 | export class ServerCookiesService extends CookiesService { 9 | private newCookies: { [name: string]: string | undefined } = {}; 10 | 11 | constructor( 12 | cookiesOptions: CookiesOptionsService, 13 | @Inject('REQUEST') private request: any, 14 | @Inject('RESPONSE') private response: any 15 | ) { 16 | super(cookiesOptions); 17 | } 18 | 19 | protected cookiesReader(): { [key: string]: any } { 20 | const allCookies: { [key: string]: any } = { 21 | ...this.request.cookies, 22 | ...this.newCookies 23 | }; 24 | const cookies: { [key: string]: any } = {}; 25 | for (const name in allCookies) { 26 | if (typeof allCookies[name] !== 'undefined') { 27 | cookies[name] = decodeURIComponent(allCookies[name]); 28 | } 29 | } 30 | return cookies; 31 | } 32 | 33 | protected cookiesWriter(): ( 34 | name: string, 35 | value: string | undefined, 36 | options?: CookiesOptions 37 | ) => void { 38 | return ( 39 | name: string, 40 | value: string | undefined, 41 | options?: CookiesOptions 42 | ) => { 43 | this.newCookies[name] = value; 44 | this.response.cookie(name, value, this.buildCookiesOptions(options)); 45 | }; 46 | } 47 | 48 | private buildCookiesOptions(options?: CookiesOptions): CookiesOptions { 49 | let opts: CookiesOptions = mergeOptions(this.options, options); 50 | if (isString(opts.expires)) { 51 | opts.expires = new Date(opts.expires); 52 | } 53 | return opts; 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@ngx-utils/cookies", 3 | "version": "4.0.0", 4 | "description": "Manage your cookies on client and server side (Angular Universal)", 5 | "keywords": [ 6 | "angular", 7 | "angular universal", 8 | "angular ssr", 9 | "ssr", 10 | "cookie", 11 | "cookies" 12 | ], 13 | "main": "bundles/cookies.umd.min.js", 14 | "module": "index.js", 15 | "typings": "index.d.ts", 16 | "scripts": { 17 | "clean:pre": "rimraf release", 18 | "clean:post": "rimraf \"src/**/*.ngfactory.ts\" \"index.ngsummary.json\" \"browser.ngsummary.json\" \"server.ngsummary.json\" \"src/**/*.ngsummary.json\"", 19 | "copy": "cpy LICENSE package.json README.md release", 20 | "build:js": "ngc", 21 | "build:umd": "rollup -c", 22 | "build:minify": "uglifyjs release/bundles/cookies.umd.js --screw-ie8 --compress --mangle --comments --output release/bundles/cookies.umd.min.js", 23 | "prebuild": "npm run clean:pre", 24 | "build": "npm run build:js && npm run build:umd && npm run build:minify", 25 | "postbuild": "npm run clean:post && npm run copy", 26 | "release": "npm run build && cd ./release && npm publish" 27 | }, 28 | "peerDependencies": { 29 | "@angular/core": "9.0.0", 30 | "rxjs": "6.5.3", 31 | "zone.js": "^0.8.19" 32 | }, 33 | "devDependencies": { 34 | "@angular/common": "9.0.0", 35 | "@angular/compiler": "9.0.0", 36 | "@angular/compiler-cli": "9.0.0", 37 | "@angular/core": "9.0.0", 38 | "cpy-cli": "^1.0.1", 39 | "rimraf": "^2.6.1", 40 | "rollup": "^0.43.0", 41 | "rxjs": "6.5.3", 42 | "typescript": "3.7.5", 43 | "uglify-js": "^3.0.18", 44 | "zone.js": "^0.8.19" 45 | }, 46 | "dependencies": {}, 47 | "repository": { 48 | "type": "git", 49 | "url": "git+https://github.com/ngx-utils/cookies.git" 50 | }, 51 | "homepage": "https://github.com/ngx-utils/cookies#readme", 52 | "bugs": { 53 | "url": "https://github.com/ngx-utils/cookies/issues" 54 | }, 55 | "author": "Anton Barada", 56 | "license": "MIT" 57 | } 58 | -------------------------------------------------------------------------------- /src/browser/browser-cookies.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | 3 | import { CookiesService } from '../cookies.service'; 4 | import { CookiesOptions } from '../cookies-options'; 5 | import { CookiesOptionsService } from '../cookies-options.service'; 6 | import { isBlank, isString, mergeOptions, safeDecodeURIComponent } from '../utils'; 7 | 8 | @Injectable() 9 | export class BrowserCookiesService extends CookiesService { 10 | get cookieString(): string { 11 | return document.cookie || ''; 12 | } 13 | 14 | set cookieString(val: string) { 15 | document.cookie = val; 16 | } 17 | 18 | constructor(cookiesOptions: CookiesOptionsService) { 19 | super(cookiesOptions); 20 | } 21 | 22 | protected cookiesReader(): { [key: string]: any } { 23 | let lastCookies = {}; 24 | let lastCookieString = ''; 25 | let cookiesArray: string[], cookie: string, i: number, index: number, name: string; 26 | let currentCookieString = this.cookieString; 27 | if (currentCookieString !== lastCookieString) { 28 | lastCookieString = currentCookieString; 29 | cookiesArray = lastCookieString.split('; '); 30 | lastCookies = {}; 31 | for (i = 0; i < cookiesArray.length; i++) { 32 | cookie = cookiesArray[i]; 33 | index = cookie.indexOf('='); 34 | if (index > 0) { // ignore nameless cookies 35 | name = safeDecodeURIComponent(cookie.substring(0, index)); 36 | if (isBlank((lastCookies)[name])) { 37 | (lastCookies)[name] = safeDecodeURIComponent(cookie.substring(index + 1)); 38 | } 39 | } 40 | } 41 | } 42 | return lastCookies; 43 | } 44 | 45 | protected cookiesWriter(): (name: string, value: string | undefined, options?: CookiesOptions) => void { 46 | return (name: string, value: string | undefined, options?: CookiesOptions) => { 47 | this.cookieString = this.buildCookieString(name, value, options); 48 | }; 49 | } 50 | 51 | private buildCookieString(name: string, value: string | undefined, options?: CookiesOptions): string { 52 | let opts: CookiesOptions = mergeOptions(this.options, options); 53 | let expires: any = opts.expires; 54 | if (isBlank(value)) { 55 | expires = 'Thu, 01 Jan 1970 00:00:00 GMT'; 56 | value = ''; 57 | } 58 | if (isString(expires)) { 59 | expires = new Date(expires); 60 | } 61 | let str = encodeURIComponent(name) + '=' + encodeURIComponent((value as string)); 62 | str += opts.path ? ';path=' + opts.path : ''; 63 | str += opts.domain ? ';domain=' + opts.domain : ''; 64 | str += expires ? ';expires=' + expires.toUTCString() : ''; 65 | str += opts.secure ? ';secure' : ''; 66 | let cookiesLength = str.length + 1; 67 | if (cookiesLength > 4096) { 68 | console.log(`Cookie \'${name}\' possibly not set or overflowed because it was too 69 | large (${cookiesLength} > 4096 bytes)!`); 70 | } 71 | return str; 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation. 6 | 7 | ## Our Standards 8 | 9 | Examples of behavior that contributes to creating a positive environment include: 10 | 11 | * Using welcoming and inclusive language 12 | * Being respectful of differing viewpoints and experiences 13 | * Gracefully accepting constructive criticism 14 | * Focusing on what is best for the community 15 | * Showing empathy towards other community members 16 | 17 | Examples of unacceptable behavior by participants include: 18 | 19 | * The use of sexualized language or imagery and unwelcome sexual attention or advances 20 | * Trolling, insulting/derogatory comments, and personal or political attacks 21 | * Public or private harassment 22 | * Publishing others' private information, such as a physical or electronic address, without explicit permission 23 | * Other conduct which could reasonably be considered inappropriate in a professional setting 24 | 25 | ## Our Responsibilities 26 | 27 | Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. 28 | 29 | Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. 30 | 31 | ## Scope 32 | 33 | This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. 34 | 35 | ## Enforcement 36 | 37 | Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at themrbarada@gmail.com. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. 38 | 39 | Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. 40 | 41 | ## Attribution 42 | 43 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version] 44 | 45 | [homepage]: http://contributor-covenant.org 46 | [version]: http://contributor-covenant.org/version/1/4/ 47 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # @ngx-utils/cookies 2 | 3 | [![npm version](https://badge.fury.io/js/%40ngx-utils%2Fcookies.svg)](https://badge.fury.io/js/%40ngx-utils%2Fcookies) [![npm downloads](https://img.shields.io/npm/dm/@ngx-utils/cookies.svg)](https://www.npmjs.com/package/@ngx-utils/cookies) 4 | 5 | Manage your cookies on client and server side (Angular Universal) 6 | 7 | Example in [@ngx-utils/universal-starter](https://github.com/ngx-utils/universal-starter/blob/master/src/app/auth-http.service.ts#L68) shows the way in which `CookiesService` is used to get access token from cookies on client and **server side**, and then set Authorization headers for all HTTP requests. 8 | 9 | ## Table of contents: 10 | 11 | - [Prerequisites](#prerequisites) 12 | - [Getting started](#getting-started) 13 | - [Installation](#installation) 14 | - [browser.module.ts](#browsermodulets) 15 | - [server.module.ts](#servermodulets) 16 | - [Cookies options](#cookies-options) 17 | - [API](#api) 18 | - [Example of usage](#example-of-usage) 19 | - [License](#license) 20 | 21 | ## Prerequisites 22 | 23 | This package depends on `@angular v9.0.0`. 24 | 25 | ## Getting started 26 | 27 | ### Installation 28 | 29 | Install **@ngx-utils/cookies** from npm: 30 | 31 | ```bash 32 | npm install @ngx-utils/cookies --save 33 | ``` 34 | 35 | ### browser.module.ts 36 | 37 | Add **BrowserCookiesModule** to your browser module: 38 | 39 | ```ts 40 | import { NgModule } from '@angular/core'; 41 | import { BrowserModule } from '@angular/platform-browser'; 42 | import { BrowserCookiesModule } from '@ngx-utils/cookies/browser'; 43 | ... 44 | import { AppModule } from './app/app.module'; 45 | import { AppComponent } from './app/app.component'; 46 | ... 47 | @NgModule({ 48 | imports: [ 49 | BrowserModule.withServerTransition({appId: 'your-app-id'}), 50 | BrowserCookiesModule.forRoot(), 51 | AppModule 52 | ... 53 | ], 54 | bootstrap: [AppComponent] 55 | }) 56 | export class BrowserAppModule { } 57 | ``` 58 | 59 | ### server.module.ts 60 | 61 | Add **ServerCookiesModule** to your server module: 62 | 63 | ```ts 64 | import { NgModule } from '@angular/core'; 65 | import { BrowserModule } from '@angular/platform-browser'; 66 | import { ServerModule } from '@angular/platform-server'; 67 | import { ServerCookiesModule } from '@ngx-utils/cookies/server'; 68 | ... 69 | import { AppModule } from './app/app.module'; 70 | import { AppComponent } from './app/app.component'; 71 | ... 72 | @NgModule({ 73 | imports: [ 74 | BrowserModule.withServerTransition({ appId: 'your-app-id' }), 75 | ServerModule, 76 | ServerCookiesModule.forRoot(), 77 | AppModule 78 | ... 79 | ], 80 | bootstrap: [AppComponent] 81 | }) 82 | export class ServerAppModule { } 83 | ``` 84 | 85 | ### Cookies options 86 | 87 | You can preset cookies options: 88 | 89 | ```ts 90 | BrowserCookiesModule.forRoot({ 91 | path: '/', 92 | domain: 'your.domain', 93 | expires: '01.01.2020', 94 | secure: true, 95 | httpOnly: true 96 | }) 97 | ... 98 | ServerCookiesModule.forRoot({ 99 | path: '/', 100 | domain: 'your.domain', 101 | expires: '01.01.2020', 102 | secure: true, 103 | httpOnly: true 104 | }) 105 | ``` 106 | 107 | ## API 108 | 109 | `CookieService` has following methods: 110 | 111 | - `put(key: string, value: string, options?: CookiesOptions): void` put some value to cookies; 112 | - `putObject(key: string, value: Object, options?: CookiesOptions): void` put object value to cookies; 113 | - `get(key: string): string` get some value from cookies by `key`; 114 | - `getObject(key: string): { [key: string]: string } | string` get object value from cookies by `key`; 115 | - `getAll(): { [key: string]: string }` get all cookies ; 116 | - `remove(key: string, options?: CookiesOptions): void` remove cookie by `key`; 117 | - `removeAll(): void` remove all cookies; 118 | 119 | ## Example of usage 120 | 121 | If you're using `express` as server then add following code to your `server.ts`: 122 | 123 | ```ts 124 | import { renderModuleFactory } from "@angular/platform-server"; 125 | import { provideModuleMap } from "@nguniversal/module-map-ngfactory-loader"; 126 | import * as cookieParser from "cookie-parser"; 127 | 128 | app.use(cookieParser("Your private token")); 129 | 130 | app.engine("html", (_, options, callback) => { 131 | renderModuleFactory(AppServerModuleNgFactory, { 132 | document: template, 133 | url: options.req.url, 134 | extraProviders: [ 135 | provideModuleMap(LAZY_MODULE_MAP), 136 | { 137 | provide: "REQUEST", 138 | useValue: options.req 139 | }, 140 | { 141 | provide: "RESPONSE", 142 | useValue: options.req.res 143 | } 144 | ] 145 | }).then(html => { 146 | callback(null, html); 147 | }); 148 | }); 149 | ``` 150 | 151 | Then just import `CookiesService` from `@ngx-utils/cookies` and use it: 152 | 153 | ```ts 154 | import { Component, OnInit } from "@angular/core"; 155 | import { CookiesService } from "@ngx-utils/cookies"; 156 | 157 | @Component({ 158 | selector: "app-root", 159 | templateUrl: "./app.component.html", 160 | styleUrls: ["./app.component.scss"] 161 | }) 162 | export class AppComponent implements OnInit { 163 | constructor(private cookies: CookiesService) {} 164 | 165 | ngOnInit() { 166 | this.cookies.put("some_cookie", "some_cookie"); 167 | this.cookies.put("http_only_cookie", "http_only_cookie", { 168 | httpOnly: true 169 | }); 170 | console.log(this.cookies.get("some_cookie"), " => some_cookie"); 171 | console.log(this.cookies.get("http_only_cookie"), " => undefined"); 172 | console.log(this.cookies.getAll()); 173 | } 174 | } 175 | ``` 176 | 177 | If you're using another framework you need to overrride `ServerCookiesService`. 178 | 179 | For example for `koa` you need add following code to your server: 180 | 181 | ```ts 182 | app.use(async (ctx: Context) => { 183 | ctx.body = await renderModuleFactory(AppServerModuleNgFactory, { 184 | document: template, 185 | url: ctx.req.url, 186 | extraProviders: [ 187 | provideModuleMap(LAZY_MODULE_MAP), 188 | { 189 | provide: "KOA_CONTEXT", 190 | useValue: ctx 191 | } 192 | ] 193 | }); 194 | }); 195 | ``` 196 | 197 | Then create `server-cookies.service.ts`: 198 | 199 | ```ts 200 | import { Context } from "koa"; 201 | import { Inject, Injectable } from "@angular/core"; 202 | import { 203 | CookiesService, 204 | CookiesOptionsService, 205 | CookiesOptions 206 | } from "@ngx-utils/cookies"; 207 | 208 | @Injectable() 209 | export class ServerCookiesService extends CookiesService { 210 | private newCookies: { [name: string]: string | undefined } = {}; 211 | 212 | constructor( 213 | cookiesOptions: CookiesOptionsService, 214 | @Inject("KOA_CONTEXT") private ctx: Context 215 | ) { 216 | super(cookiesOptions); 217 | } 218 | 219 | get(key: string): string { 220 | return this.newCookies[key] || this.ctx.cookies.get(key); 221 | } 222 | 223 | protected cookiesReader() { 224 | return {}; 225 | } 226 | 227 | protected cookiesWriter(): ( 228 | name: string, 229 | value: string | undefined, 230 | options?: CookiesOptions 231 | ) => void { 232 | return (name: string, value: string | undefined, options?: any) => { 233 | this.newCookies[name] = value; 234 | this.ctx.cookies.set(name, value, { httpOnly: false, ...options }); 235 | }; 236 | } 237 | } 238 | ``` 239 | 240 | And add `server-cookies.service.ts` to `app.server.module.ts`: 241 | 242 | ```ts 243 | { 244 | provide: CookiesService, 245 | useClass: ServerCookiesService, 246 | }, 247 | ``` 248 | 249 | ## License 250 | 251 | The MIT License (MIT) 252 | --------------------------------------------------------------------------------