diff --git a/.eslintrc.js b/.eslintrc.js index 09f061e..b1a01a9 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -1,6 +1,6 @@ module.exports = { env: { - node: true, + node: true }, extends: [ 'eslint:recommended', @@ -10,47 +10,44 @@ module.exports = { 'plugin:import/errors', 'plugin:import/warnings', 'plugin:import/typescript', - 'prettier', - ], - plugins: [ - 'import', - '@typescript-eslint' + 'prettier' ], + plugins: ['import', '@typescript-eslint'], settings: { 'import/parsers': { - '@typescript-eslint/parser': ['.ts'], + '@typescript-eslint/parser': ['.ts'] }, 'import/resolver': { node: { extensions: ['.js', '.ts'], - moduleDirectory: ['node_modules', 'src/'], + moduleDirectory: ['node_modules', 'src/'] }, typescript: { alwaysTryTypes: true, - project: '.', - }, - }, + project: '.' + } + } }, overrides: [ { env: { - jest: true, + jest: true }, files: ['**/__tests__/**/*.[jt]s', '**/?(*.)+(spec|test).[jt]s'], extends: ['plugin:jest/recommended'], rules: { 'import/no-extraneous-dependencies': [ 'off', - { devDependencies: ['**/?(*.)+(spec|test).[jt]s'] }, + { devDependencies: ['**/?(*.)+(spec|test).[jt]s'] } ], - camelcase: ['off'], - }, - }, + camelcase: ['off'] + } + } ], ignorePatterns: ['**/*.js', 'node_modules', 'dist'], parserOptions: { root: true, tsconfigRootDir: __dirname, - project: ['./tsconfig.json'], - }, + project: ['./tsconfig.json'] + } } diff --git a/.github/workflows/close-inactive.yaml b/.github/workflows/close-inactive.yaml index f6a4ce3..6598896 100644 --- a/.github/workflows/close-inactive.yaml +++ b/.github/workflows/close-inactive.yaml @@ -2,7 +2,7 @@ name: Close inactive issues and PRs on: workflow_dispatch: schedule: - - cron: "30 1 * * *" + - cron: '30 1 * * *' jobs: close-stale: @@ -15,16 +15,16 @@ jobs: with: days-before-stale: 30 days-before-close: 14 - stale-issue-label: "stale" - stale-pr-label: "stale" + stale-issue-label: 'stale' + stale-pr-label: 'stale' exempt-issue-labels: backlog,triage,nostale exempt-pr-labels: backlog,triage,nostale - stale-pr-message: "This PR is stale because it has been open for 30 days with no activity." - close-pr-message: "This PR was closed because it has been inactive for 14 days since being marked as stale." + stale-pr-message: 'This PR is stale because it has been open for 30 days with no activity.' + close-pr-message: 'This PR was closed because it has been inactive for 14 days since being marked as stale.' - stale-issue-message: "This issue is stale because it has been open for 30 days with no activity." - close-issue-message: "This issue was closed because it has been inactive for 14 days since being marked as stale." + stale-issue-message: 'This issue is stale because it has been open for 30 days with no activity.' + close-issue-message: 'This issue was closed because it has been inactive for 14 days since being marked as stale.' repo-token: ${{ secrets.GITHUB_TOKEN }} diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 0000000..4d173ba --- /dev/null +++ b/.prettierignore @@ -0,0 +1,2 @@ +static +/node_modules \ No newline at end of file diff --git a/README.md b/README.md index 2efb827..c65b75d 100644 --- a/README.md +++ b/README.md @@ -1,15 +1,15 @@ # Haste Haste is an open-source pastebin software written in node.js, which is easily -installable in any network. It can be backed by either redis or filesystem, -and has a very easy adapter interface for other stores. A publicly available +installable in any network. It can be backed by either redis or filesystem, +and has a very easy adapter interface for other stores. A publicly available version can be found at [hastebin.com](http://hastebin.com) Major design objectives: -* Be really pretty -* Be really simple -* Be easy to set up and use +- Be really pretty +- Be really simple +- Be easy to set up and use Haste works really well with a little utility called [haste-client](https://github.com/seejohnrun/haste-client), allowing you @@ -18,58 +18,58 @@ to do things like: `cat something | haste` which will output a URL to share containing the contents of `cat something`'s -STDOUT. Check the README there for more details and usages. +STDOUT. Check the README there for more details and usages. ## Tested Browsers -* Firefox 8 -* Chrome 17 -* Safari 5.3 +- Firefox 8 +- Chrome 17 +- Safari 5.3 ## Installation 1. Download the package, and expand it -3. `yarn add` +2. `yarn` ## Running the project -> Explore the settings inside of config.js, but the defaults should be good +> Explore the settings inside of project-config.js, but the defaults should be good ### Development -1. `yarn add` +1. `yarn` 2. `yarn dev` (you may specify an optional `` as well) ### Production -1. `yarn add` +1. `yarn` 2. `yarn build` to build the package 3. `yarn start` to start the server ### Production with Docker -1. `docker compose up` +1. `docker-compose up` ## Settings -* `host` - the host the server runs on (default localhost) -* `port` - the port the server runs on (default 7777) -* `keyLength` - the length of the keys to user (default 10) -* `maxLength` - maximum length of a paste (default 400000) -* `staticMaxAge` - max age for static assets (86400) -* `recompressStaticAssets` - whether or not to compile static js assets (true) -* `documents` - static documents to serve (ex: http://hastebin.com/about.com) - in addition to static assets. These will never expire. -* `storage` - storage options (see below) -* `logging` - logging preferences -* `keyGenerator` - key generator options (see below) -* `rateLimits` - settings for rate limiting (see below) +- `host` - the host the server runs on (default localhost) +- `port` - the port the server runs on (default 7777) +- `keyLength` - the length of the keys to user (default 10) +- `maxLength` - maximum length of a paste (default 400000) +- `staticMaxAge` - max age for static assets (86400) +- `recompressStaticAssets` - whether or not to compile static js assets (true) +- `documents` - static documents to serve (ex: http://hastebin.com/about.com) + in addition to static assets. These will never expire. +- `storage` - storage options (see below) +- `logging` - logging preferences +- `keyGenerator` - key generator options (see below) +- `rateLimits` - settings for rate limiting (see below) ## Rate Limiting When present, the `rateLimits` option enables built-in rate limiting courtesy -of `connect-ratelimit`. Any of the options supported by that library can be -used and set in `config.js`. +of `connect-ratelimit`. Any of the options supported by that library can be +used and set in `project-config.js`. See the README for [connect-ratelimit](https://github.com/dharmafly/connect-ratelimit) for more information! @@ -80,7 +80,7 @@ for more information! Attempts to generate phonetic keys, similar to `pwgen` -``` json +```json { "type": "phonetic" } @@ -90,7 +90,7 @@ Attempts to generate phonetic keys, similar to `pwgen` Generates a random key -``` json +```json { "type": "random", "keyspace": "abcdef" @@ -104,10 +104,10 @@ for the key. ### File -To use file storage (the default) change the storage section in `config.js` to +To use file storage (the default) change the storage section in `project-config.js` to something like: -``` json +```json { "path": "./data", "type": "file" @@ -127,7 +127,7 @@ To use redis storage you must install the `redis` package in npm, and have Once you've done that, your config section should look like: -``` json +```json { "type": "redis", "host": "localhost", @@ -152,7 +152,7 @@ To use postgres storage you must install the `pg` package in npm Once you've done that, your config section should look like: -``` json +```json { "type": "postgres", "connectionUrl": "postgres://user:password@host:5432/database" @@ -179,7 +179,7 @@ To use mongodb storage you must install the 'mongodb' package in npm Once you've done that, your config section should look like: -``` json +```json { "type": "mongo", "connectionUrl": "mongodb://localhost:27017/database" @@ -201,7 +201,7 @@ To use memcache storage you must install the `memcached` package via npm Once you've done that, your config section should look like: -``` json +```json { "type": "memcached", "host": "127.0.0.1", @@ -223,7 +223,7 @@ To use the RethinkDB storage system, you must install the `rethinkdbdash` packag Once you've done that, your config section should look like this: -``` json +```json { "type": "rethinkdb", "host": "127.0.0.1", @@ -245,7 +245,7 @@ To use the Google Datastore storage system, you must install the `@google-cloud/ Once you've done that, your config section should look like this: -``` json +```json { "type": "google-datastore" } @@ -277,17 +277,14 @@ your bucket: ```json { - "Version": "2012-10-17", - "Statement": [ - { - "Action": [ - "s3:GetObject", - "s3:PutObject" - ], - "Effect": "Allow", - "Resource": "arn:aws:s3:::your-bucket-name-goes-here/*" - } - ] + "Version": "2012-10-17", + "Statement": [ + { + "Action": ["s3:GetObject", "s3:PutObject"], + "Effect": "Allow", + "Resource": "arn:aws:s3:::your-bucket-name-goes-here/*" + } + ] } ``` @@ -401,6 +398,6 @@ SOFTWARE ### Other components: -* jQuery: MIT/GPL license -* highlight.js: Copyright © 2006, Ivan Sagalaev -* highlightjs-coffeescript: WTFPL - Copyright © 2011, Dmytrii Nagirniak +- jQuery: MIT/GPL license +- highlight.js: Copyright © 2006, Ivan Sagalaev +- highlightjs-coffeescript: WTFPL - Copyright © 2011, Dmytrii Nagirniak diff --git a/about.md b/about.md index c446934..3ef7c3d 100644 --- a/about.md +++ b/about.md @@ -8,7 +8,7 @@ Haste is the prettiest, easiest to use pastebin ever made. ## Basic Usage -Type what you want me to see, click "Save", and then copy the URL. Send that +Type what you want me to see, click "Save", and then copy the URL. Send that URL to someone and they'll see what you see. To make a new entry, click "New" (or type 'control + n') @@ -16,7 +16,7 @@ To make a new entry, click "New" (or type 'control + n') ## From the Console Most of the time I want to show you some text, it's coming from my current -console session. We should make it really easy to take code from the console +console session. We should make it really easy to take code from the console and send it to people. `cat something | haste` # https://hastebin.com/1238193 @@ -24,27 +24,28 @@ and send it to people. You can even take this a step further, and cut out the last step of copying the URL with: -* osx: `cat something | haste | pbcopy` -* linux: `cat something | haste | xsel` -* windows: check out [WinHaste](https://github.com/ajryan/WinHaste) +- osx: `cat something | haste | pbcopy` +- linux: `cat something | haste | xsel` +- windows: check out [WinHaste](https://github.com/ajryan/WinHaste) After running that, the STDOUT output of `cat something` will show up at a URL which has been conveniently copied to your clipboard. That's all there is to that, and you can install it with `gem install haste` right now. - * osx: you will need to have an up to date version of Xcode - * linux: you will need to have rubygems and ruby-devel installed + +- osx: you will need to have an up to date version of Xcode +- linux: you will need to have rubygems and ruby-devel installed ## Duration -Pastes will stay for 30 days from their last view. They may be removed earlier +Pastes will stay for 30 days from their last view. They may be removed earlier and without notice. ## Privacy While the contents of hastebin.com are not directly crawled by any search robot -that obeys "robots.txt", there should be no great expectation of privacy. Post +that obeys "robots.txt", there should be no great expectation of privacy. Post things at your own risk. Not responsible for any loss of data or removed pastes. @@ -52,8 +53,8 @@ pastes. Haste can easily be installed behind your network, and it's all open source! -* [haste-client](https://github.com/seejohnrun/haste-client) -* [haste-server](https://github.com/seejohnrun/haste-server) +- [haste-client](https://github.com/seejohnrun/haste-client) +- [haste-server](https://github.com/seejohnrun/haste-server) ## Author diff --git a/config/jest.config.js b/config/jest.config.js index 2e570d6..f89435c 100644 --- a/config/jest.config.js +++ b/config/jest.config.js @@ -5,10 +5,8 @@ module.exports = { rootDir: '../', testRegex: '\\.test\\.ts$', reporters: ['default'], - roots: [ - "test" - ], + roots: ['test'], moduleNameMapper: { - "src/(.*)": "/src/$1" + 'src/(.*)': '/src/$1' } } diff --git a/docker-entrypoint.js b/docker-entrypoint.js index 9454484..18a1dd8 100644 --- a/docker-entrypoint.js +++ b/docker-entrypoint.js @@ -28,8 +28,8 @@ const { RATE_LIMITS_BLACKLIST_TOTAL_REQUESTS, RATE_LIMITS_BLACKLIST_EVERY_MILLISECONDS, RATE_LIMITS_BLACKLIST, - DOCUMENTS, -} = process.env; + DOCUMENTS +} = process.env const config = { host: HOST, @@ -47,29 +47,29 @@ const config = { { level: LOGGING_LEVEL, type: LOGGING_TYPE, - colorize: LOGGING_COLORIZE, - }, + colorize: LOGGING_COLORIZE + } ], keyGenerator: { type: KEYGENERATOR_TYPE, - keyspace: KEY_GENERATOR_KEYSPACE, + keyspace: KEY_GENERATOR_KEYSPACE }, rateLimits: { - whitelist: RATE_LIMITS_WHITELIST ? RATE_LIMITS_WHITELIST.split(",") : [], - blacklist: RATE_LIMITS_BLACKLIST ? RATE_LIMITS_BLACKLIST.split(",") : [], + whitelist: RATE_LIMITS_WHITELIST ? RATE_LIMITS_WHITELIST.split(',') : [], + blacklist: RATE_LIMITS_BLACKLIST ? RATE_LIMITS_BLACKLIST.split(',') : [], categories: { normal: { totalRequests: RATE_LIMITS_NORMAL_TOTAL_REQUESTS, - every: RATE_LIMITS_NORMAL_EVERY_MILLISECONDS, + every: RATE_LIMITS_NORMAL_EVERY_MILLISECONDS }, whitelist: RATE_LIMITS_WHITELIST_EVERY_MILLISECONDS || RATE_LIMITS_WHITELIST_TOTAL_REQUESTS ? { totalRequests: RATE_LIMITS_WHITELIST_TOTAL_REQUESTS, - every: RATE_LIMITS_WHITELIST_EVERY_MILLISECONDS, + every: RATE_LIMITS_WHITELIST_EVERY_MILLISECONDS } : null, blacklist: @@ -77,10 +77,10 @@ const config = { RATE_LIMITS_BLACKLIST_TOTAL_REQUESTS ? { totalRequests: RATE_LIMITS_WHITELIST_TOTAL_REQUESTS, - every: RATE_LIMITS_BLACKLIST_EVERY_MILLISECONDS, + every: RATE_LIMITS_BLACKLIST_EVERY_MILLISECONDS } - : null, - }, + : null + } }, storage: { @@ -94,15 +94,15 @@ const config = { db: STORAGE_DB, user: STORAGE_USERNAME, password: STORAGE_PASSWORD, - path: STORAGE_FILEPATH, + path: STORAGE_FILEPATH }, documents: DOCUMENTS - ? DOCUMENTS.split(",").reduce((acc, item) => { - const keyAndValueArray = item.replace(/\s/g, "").split("="); - return { ...acc, [keyAndValueArray[0]]: keyAndValueArray[1] }; + ? DOCUMENTS.split(',').reduce((acc, item) => { + const keyAndValueArray = item.replace(/\s/g, '').split('=') + return { ...acc, [keyAndValueArray[0]]: keyAndValueArray[1] } }, {}) - : null, -}; + : null +} -console.log(JSON.stringify(config)); +console.log(JSON.stringify(config)) diff --git a/package.json b/package.json index 12f0213..011bf7e 100644 --- a/package.json +++ b/package.json @@ -95,6 +95,6 @@ "dev": "nodemon", "lint": "eslint src --fix", "types:check": "tsc --noEmit --pretty", - "pretty": "prettier --write src" + "pretty": "prettier --write ." } } diff --git a/src/lib/document-handler/index.ts b/src/lib/document-handler/index.ts index f725f5c..dd67505 100644 --- a/src/lib/document-handler/index.ts +++ b/src/lib/document-handler/index.ts @@ -12,7 +12,7 @@ class DocumentHandler { maxLength?: number - public store: Store + store: Store keyGenerator: KeyGenerator @@ -55,7 +55,7 @@ class DocumentHandler { ) } - public handlePost(request: Request, response: Response) { + handlePost(request: Request, response: Response) { // const this = this let buffer = '' let cancelled = false @@ -121,7 +121,7 @@ class DocumentHandler { } } - public handleRawGet(request: Request, response: Response) { + handleRawGet(request: Request, response: Response) { const key = request.params.id.split('.')[0] const skipExpire = !!this.config.documents[key] diff --git a/src/types/config.ts b/src/types/config.ts index a02c1e5..6afbbb8 100644 --- a/src/types/config.ts +++ b/src/types/config.ts @@ -23,29 +23,29 @@ export type BaseStoreConfig = { export interface MongoStoreConfig extends BaseStoreConfig { connectionUrl: string - type: StoreNames.mongo + type: StoreNames.Mongo } export interface MemcachedStoreConfig extends BaseStoreConfig { host: string port: number - type: StoreNames.memcached + type: StoreNames.Memcached } export interface FileStoreConfig extends BaseStoreConfig { path: string - type: StoreNames.file + type: StoreNames.File } export interface AmazonStoreConfig extends BaseStoreConfig { bucket: string region: string - type: StoreNames.amazons3 + type: StoreNames.AmazonS3 } export interface PostgresStoreConfig extends BaseStoreConfig { connectionUrl: string - type: StoreNames.postgres + type: StoreNames.Postgres } export interface RethinkDbStoreConfig extends BaseStoreConfig { @@ -54,7 +54,7 @@ export interface RethinkDbStoreConfig extends BaseStoreConfig { db: string user: string password: string - type: StoreNames.rethinkdb + type: StoreNames.RethinkDb } export interface RedisStoreConfig extends BaseStoreConfig { @@ -65,11 +65,11 @@ export interface RedisStoreConfig extends BaseStoreConfig { password?: string host?: string port?: string - type: StoreNames.redis + type: StoreNames.Redis } export interface GoogleStoreConfig extends BaseStoreConfig { - type: StoreNames.googledatastore + type: StoreNames.GoogleDataStore } export type StoreConfig = diff --git a/src/types/store-names.ts b/src/types/store-names.ts index d3495f8..e2aca14 100644 --- a/src/types/store-names.ts +++ b/src/types/store-names.ts @@ -1,11 +1,11 @@ // eslint-disable-next-line import/prefer-default-export export enum StoreNames { - amazons3 = 'amazon-s3', - file = 'file', - googledatastore = 'google-datastore', - memcached = 'memcached', - mongo = 'mongo', - postgres = 'postgres', - redis = 'redis', - rethinkdb = 'rethinkdb' + AmazonS3 = 'amazon-s3', + File = 'file', + GoogleDataStore = 'google-datastore', + Memcached = 'memcached', + Mongo = 'mongo', + Postgres = 'postgres', + Redis = 'redis', + RethinkDb = 'rethinkdb' } diff --git a/test/document-handler/index.test.ts b/test/document-handler/index.test.ts index 47b4b65..1e141ba 100644 --- a/test/document-handler/index.test.ts +++ b/test/document-handler/index.test.ts @@ -1,25 +1,35 @@ -import { createMock } from 'ts-auto-mock'; +import { createMock } from 'ts-auto-mock' import DocumentHandler from 'src/lib/document-handler/index' import Generator from 'src/lib/key-generators/random' import constants from 'src/constants' import { Config } from 'src/types/config' -import { Store } from 'src/lib/document-stores'; +import { Store } from 'src/lib/document-stores' -const store : Store = createMock(); -const config : Config = createMock(); +const store: Store = createMock() +const config: Config = createMock() describe('document-handler', () => { describe('with random key', () => { it('should choose a key of the proper length', () => { const gen = new Generator({ type: 'random' }) - const dh = new DocumentHandler({ keyLength: 6, keyGenerator: gen, store, config}) - expect(dh.acceptableKey()?.length).toEqual(6); + const dh = new DocumentHandler({ + keyLength: 6, + keyGenerator: gen, + store, + config + }) + expect(dh.acceptableKey()?.length).toEqual(6) }) it('should choose a default key length', () => { const gen = new Generator({ type: 'random' }) - const dh = new DocumentHandler({ keyGenerator: gen, maxLength: 1, store, config }) - expect(dh.keyLength).toEqual(constants.DEFAULT_KEY_LENGTH); + const dh = new DocumentHandler({ + keyGenerator: gen, + maxLength: 1, + store, + config + }) + expect(dh.keyLength).toEqual(constants.DEFAULT_KEY_LENGTH) }) }) }) diff --git a/test/document-stores/redis.test.ts b/test/document-stores/redis.test.ts index 9a98efa..62f63d2 100644 --- a/test/document-stores/redis.test.ts +++ b/test/document-stores/redis.test.ts @@ -14,7 +14,7 @@ describe('Redis document store', () => { it('should be able to set a key and have an expiration set', async () => { store = new RedisDocumentStore({ expire: 10, - type: StoreNames.redis + type: StoreNames.Redis }) return store.set('hello1', 'world', async () => { const res = await store.client?.ttl('hello1') @@ -25,7 +25,7 @@ describe('Redis document store', () => { it('should not set an expiration when told not to', async () => { store = new RedisDocumentStore({ expire: 10, - type: StoreNames.redis + type: StoreNames.Redis }) store.set( @@ -41,7 +41,7 @@ describe('Redis document store', () => { it('should not set an expiration when expiration is off', async () => { store = new RedisDocumentStore({ - type: StoreNames.redis + type: StoreNames.Redis }) store.set('hello3', 'world', async () => { diff --git a/test/key-generators/phonetic.test.ts b/test/key-generators/phonetic.test.ts index 74dcf45..1d60f61 100644 --- a/test/key-generators/phonetic.test.ts +++ b/test/key-generators/phonetic.test.ts @@ -1,22 +1,22 @@ /* eslint-disable jest/no-conditional-expect */ import Generator from 'src/lib/key-generators/phonetic' -const vowels = 'aeiou'; -const consonants = 'bcdfghjklmnpqrstvwxyz'; +const vowels = 'aeiou' +const consonants = 'bcdfghjklmnpqrstvwxyz' describe('PhoneticKeyGenerator', () => { describe('generation', () => { it('should return a key of the proper length', () => { - const gen = new Generator({ type: 'phonetic'}); - expect(gen.createKey(6).length).toEqual(6); - }); + const gen = new Generator({ type: 'phonetic' }) + expect(gen.createKey(6).length).toEqual(6) + }) it('should alternate consonants and vowels', () => { - const gen = new Generator({ type: 'phonetic'}); - const key = gen.createKey(3); + const gen = new Generator({ type: 'phonetic' }) + const key = gen.createKey(3) // if it starts with a consonant, we expect cvc // if it starts with a vowel, we expect vcv - if(consonants.includes(key[0])) { + if (consonants.includes(key[0])) { expect(consonants.includes(key[0])).toBeTruthy() expect(consonants.includes(key[2])).toBeTruthy() expect(vowels.includes(key[1])).toBeTruthy() @@ -25,6 +25,6 @@ describe('PhoneticKeyGenerator', () => { expect(vowels.includes(key[2])).toBeTruthy() expect(consonants.includes(key[1])).toBeTruthy() } - }); - }); -}); + }) + }) +})