├── LICENSE ├── README.md └── backend ├── .dockerignore ├── .env.sample ├── .eslintrc.js ├── .gitignore ├── .prettierrc.js ├── README.md ├── db ├── migrations │ ├── 20200325085741_initial.js │ └── 20200409085456_item-table.js ├── seeds │ ├── 00_initial.js │ └── 01_initial_beans.js └── sources │ ├── countries.csv │ └── us_states.csv ├── docker-compose.yml ├── jest.config.js ├── knexfile.js ├── package-lock.json ├── package.json └── src ├── api ├── addresses │ ├── address.schema.json │ ├── addresses.model.js │ ├── addresses.routes.js │ └── addresses.test.js ├── api.test.js ├── auth │ ├── auth.middlewares.js │ └── auth.routes.js ├── companies │ ├── companies.model.js │ ├── companies.routes.js │ ├── companies.schema.json │ └── companies.test.js ├── index.js ├── items │ ├── item_infos │ │ ├── item_infos.model.js │ │ ├── item_infos.routes.js │ │ └── item_infos.schema.json │ ├── items.model.js │ ├── items.routes.js │ ├── items.schema.json │ └── items.test.js ├── states │ ├── states.queries.js │ ├── states.routes.js │ └── states.test.js └── users │ ├── users.model.js │ ├── users.routes.js │ ├── users.schema.json │ └── users.test.js ├── app.js ├── app.test.js ├── constants ├── countries.js ├── project.js ├── tableNames.js └── us_states.js ├── db.js ├── index.js ├── lib ├── jwt.js ├── logger.js └── tableUtils.js ├── middlewares.js ├── setupFilesAfterEnv.js ├── setupTests.js └── teardownTests.js /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License Copyright (c) 2020 Coding Garden 2 | 3 | Permission is hereby granted, free 4 | of charge, to any person obtaining a copy of this software and associated 5 | documentation files (the "Software"), to deal in the Software without 6 | restriction, including without limitation the rights to use, copy, modify, merge, 7 | publish, distribute, sublicense, and/or sell copies of the Software, and to 8 | permit persons to whom the Software is furnished to do so, subject to the 9 | following conditions: 10 | 11 | The above copyright notice and this permission notice 12 | (including the next paragraph) shall be included in all copies or substantial 13 | portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 16 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 17 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO 18 | EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR 19 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 20 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Model a SQL Database 2 | 3 | See the [backend](./backend) folder for more info about the DB. 4 | -------------------------------------------------------------------------------- /backend/.dockerignore: -------------------------------------------------------------------------------- 1 | node_modules -------------------------------------------------------------------------------- /backend/.env.sample: -------------------------------------------------------------------------------- 1 | POSTGRES_PASSWORD=admin 2 | POSTGRES_USER=admin 3 | POSTGRES_DB=inventory_app 4 | JWT_SECRET=keyboard_cat -------------------------------------------------------------------------------- /backend/.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | env: { 4 | commonjs: true, 5 | es6: true, 6 | node: true, 7 | jest: true, 8 | }, 9 | extends: ['airbnb-base', 'prettier'], 10 | plugins: ['prettier'], 11 | ignorePatterns: ['node_modules/**/*.js'], 12 | globals: { 13 | Atomics: 'readonly', 14 | SharedArrayBuffer: 'readonly', 15 | }, 16 | parserOptions: { 17 | ecmaVersion: 2018, 18 | }, 19 | rules: { 20 | 'prettier/prettier': 'error', 21 | camelcase: 0, 22 | 'no-param-reassign': 0, 23 | 'no-return-assign': 0, 24 | 'no-underscore-dangle': 0, 25 | 'no-plusplus': 0, 26 | }, 27 | }; 28 | -------------------------------------------------------------------------------- /backend/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | lerna-debug.log* 8 | 9 | # Diagnostic reports (https://nodejs.org/api/report.html) 10 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 11 | 12 | # Runtime data 13 | pids 14 | *.pid 15 | *.seed 16 | *.pid.lock 17 | 18 | # Directory for instrumented libs generated by jscoverage/JSCover 19 | lib-cov 20 | 21 | # Coverage directory used by tools like istanbul 22 | coverage 23 | *.lcov 24 | 25 | # nyc test coverage 26 | .nyc_output 27 | 28 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 29 | .grunt 30 | 31 | # Bower dependency directory (https://bower.io/) 32 | bower_components 33 | 34 | # node-waf configuration 35 | .lock-wscript 36 | 37 | # Compiled binary addons (https://nodejs.org/api/addons.html) 38 | build/Release 39 | 40 | # Dependency directories 41 | node_modules/ 42 | jspm_packages/ 43 | 44 | # Snowpack dependency directory (https://snowpack.dev/) 45 | web_modules/ 46 | 47 | # TypeScript cache 48 | *.tsbuildinfo 49 | 50 | # Optional npm cache directory 51 | .npm 52 | 53 | # Optional eslint cache 54 | .eslintcache 55 | 56 | # Microbundle cache 57 | .rpt2_cache/ 58 | .rts2_cache_cjs/ 59 | .rts2_cache_es/ 60 | .rts2_cache_umd/ 61 | 62 | # Optional REPL history 63 | .node_repl_history 64 | 65 | # Output of 'npm pack' 66 | *.tgz 67 | 68 | # Yarn Integrity file 69 | .yarn-integrity 70 | 71 | # dotenv environment variables file 72 | .env 73 | .env.test 74 | 75 | # parcel-bundler cache (https://parceljs.org/) 76 | .cache 77 | .parcel-cache 78 | 79 | # Next.js build output 80 | .next 81 | 82 | # Nuxt.js build / generate output 83 | .nuxt 84 | dist 85 | 86 | # Gatsby files 87 | .cache/ 88 | # Comment in the public line in if your project uses Gatsby and not Next.js 89 | # https://nextjs.org/blog/next-9-1#public-directory-support 90 | # public 91 | 92 | # vuepress build output 93 | .vuepress/dist 94 | 95 | # Serverless directories 96 | .serverless/ 97 | 98 | # FuseBox cache 99 | .fusebox/ 100 | 101 | # DynamoDB Local files 102 | .dynamodb/ 103 | 104 | # TernJS port file 105 | .tern-port 106 | 107 | # Stores VSCode versions used for testing VSCode extensions 108 | .vscode-test 109 | 110 | # yarn v2 111 | 112 | .yarn/cache 113 | .yarn/unplugged 114 | .yarn/build-state.yml 115 | .pnp.* 116 | 117 | docker-data -------------------------------------------------------------------------------- /backend/.prettierrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | trailingComma: 'es5', 3 | tabWidth: 2, 4 | semi: true, 5 | singleQuote: true, 6 | }; 7 | -------------------------------------------------------------------------------- /backend/README.md: -------------------------------------------------------------------------------- 1 | # Model a SQL Database 2 | 3 | Every Record will have: 4 | Created At - datetime 5 | Updated At - datetime 6 | Deleted At - datetime 7 | 8 | ## Entities in an Home Inventory System 9 | 10 | * [x] User 11 | * [x] Item 12 | * [x] Item Type 13 | * [x] Manufacturer 14 | * [x] Item Location 15 | * [ ] Item Purchase Location 16 | * [ ] Comment 17 | * [ ] Warranty 18 | 19 | ## Seed the Database 20 | 21 | * [x] User 22 | * [ ] Countries - Partial, more to do! 23 | * [ ] US States - Partial, more to do! 24 | * [ ] Item Types 25 | * [ ] Location 26 | 27 | ## API Endpoints 28 | 29 | * [ ] Addresses 30 | * [x] Create 31 | * [x] List 32 | * [ ] Get One 33 | * [ ] Update 34 | * [ ] Delete 35 | * [ ] Companies 36 | * [x] List 37 | * [ ] Get One 38 | * [ ] Create 39 | * [ ] Update 40 | * [ ] Delete 41 | * [ ] Items 42 | * [x] List 43 | * [x] Create 44 | * [ ] Get One 45 | * [ ] Update 46 | * [ ] Delete 47 | * [ ] Item Info 48 | * [x] List 49 | * [x] Create 50 | * [ ] Get One 51 | * [ ] Update 52 | * [ ] Delete 53 | * [ ] Pagination -------------------------------------------------------------------------------- /backend/db/migrations/20200325085741_initial.js: -------------------------------------------------------------------------------- 1 | const tableNames = require('../../src/constants/tableNames'); 2 | const { 3 | addDefaultColumns, 4 | createNameTable, 5 | url, 6 | email, 7 | references, 8 | } = require('../../src/lib/tableUtils'); 9 | 10 | /** 11 | * @param {import('knex')} knex 12 | */ 13 | exports.up = async (knex) => { 14 | await Promise.all([ 15 | knex.schema.createTable(tableNames.user, (table) => { 16 | table.increments().notNullable(); 17 | email(table, 'email').notNullable().unique(); 18 | table.string('name').notNullable(); 19 | table.string('password', 127).notNullable(); 20 | table.datetime('last_login'); 21 | addDefaultColumns(table); 22 | }), 23 | createNameTable(knex, tableNames.item_type), 24 | createNameTable(knex, tableNames.country), 25 | createNameTable(knex, tableNames.state), 26 | createNameTable(knex, tableNames.shape), 27 | knex.schema.createTable(tableNames.inventory_location, (table) => { 28 | table.increments().notNullable(); 29 | table.string('name').notNullable().unique(); 30 | table.string('description', 1000); 31 | url(table, 'image_url'); 32 | addDefaultColumns(table); 33 | }), 34 | ]); 35 | 36 | await knex.schema.createTable(tableNames.address, (table) => { 37 | table.increments().notNullable(); 38 | table.string('street_address_1', 50).notNullable(); 39 | table.string('street_address_2', 50); 40 | table.string('city', 50).notNullable(); 41 | table.string('zipcode', 15).notNullable(); 42 | table.double('latitude').notNullable(); 43 | table.double('longitude').notNullable(); 44 | references(table, 'state', false); 45 | references(table, 'country'); 46 | addDefaultColumns(table); 47 | table.unique([ 48 | 'street_address_1', 49 | 'street_address_2', 50 | 'city', 51 | 'zipcode', 52 | 'country_id', 53 | 'state_id', 54 | ]); 55 | }); 56 | 57 | await knex.schema.createTable(tableNames.company, (table) => { 58 | table.increments().notNullable(); 59 | table.string('name').notNullable(); 60 | url(table, 'logo_url'); 61 | table.string('description', 1000); 62 | url(table, 'website_url'); 63 | // `type` text, 64 | email(table, 'email'); 65 | references(table, 'address'); 66 | addDefaultColumns(table); 67 | }); 68 | }; 69 | 70 | exports.down = async (knex) => { 71 | await Promise.all( 72 | [ 73 | tableNames.company, 74 | tableNames.address, 75 | tableNames.user, 76 | tableNames.item_type, 77 | tableNames.country, 78 | tableNames.state, 79 | tableNames.shape, 80 | tableNames.inventory_location, 81 | ].map((tableName) => knex.schema.dropTableIfExists(tableName)) 82 | ); 83 | }; 84 | -------------------------------------------------------------------------------- /backend/db/migrations/20200409085456_item-table.js: -------------------------------------------------------------------------------- 1 | const { 2 | addDefaultColumns, 3 | url, 4 | references, 5 | } = require('../../src/lib/tableUtils'); 6 | const tableNames = require('../../src/constants/tableNames'); 7 | 8 | /** 9 | * @param {import('knex')} knex 10 | */ 11 | exports.up = async (knex) => { 12 | await knex.schema.table(tableNames.state, (table) => { 13 | table.string('code'); 14 | references(table, tableNames.country); 15 | }); 16 | 17 | await knex.schema.table(tableNames.country, (table) => { 18 | table.string('code'); 19 | }); 20 | 21 | await knex.schema.createTable(tableNames.size, (table) => { 22 | table.increments(); 23 | table.string('name').notNullable(); 24 | table.float('length'); 25 | table.float('width'); 26 | table.float('height'); 27 | table.float('volume'); 28 | references(table, tableNames.shape); 29 | addDefaultColumns(table); 30 | }); 31 | 32 | await knex.schema.createTable(tableNames.item, (table) => { 33 | table.increments(); 34 | references(table, tableNames.user); 35 | table.string('name'); 36 | references(table, tableNames.item_type); 37 | table.text('description'); 38 | references(table, tableNames.company); 39 | references(table, tableNames.size, false); 40 | table.string('sku', 42); 41 | table.boolean('sparks_joy').defaultTo(true); 42 | addDefaultColumns(table); 43 | }); 44 | 45 | await knex.schema.createTable(tableNames.item_info, (table) => { 46 | table.increments(); 47 | references(table, tableNames.user); 48 | references(table, tableNames.item); 49 | table.dateTime('purchase_date').notNullable(); 50 | table.dateTime('expiration_date'); 51 | references(table, tableNames.company, false, 'retailer'); 52 | table.dateTime('last_used'); 53 | table.float('purchase_price').notNullable().defaultTo(0); 54 | table.float('msrp').notNullable().defaultTo(0); 55 | references(table, tableNames.inventory_location); 56 | addDefaultColumns(table); 57 | }); 58 | 59 | await knex.schema.createTable(tableNames.item_image, (table) => { 60 | table.increments(); 61 | references(table, tableNames.item); 62 | url(table, 'image_url'); 63 | addDefaultColumns(table); 64 | }); 65 | 66 | await knex.schema.createTable(tableNames.related_item, (table) => { 67 | table.increments(); 68 | references(table, tableNames.item); 69 | references(table, tableNames.item, false, 'related_item'); 70 | addDefaultColumns(table); 71 | }); 72 | }; 73 | 74 | /** 75 | * @param {Knex} knex 76 | */ 77 | exports.down = async (knex) => { 78 | await knex.schema.table(tableNames.state, (table) => { 79 | table.dropColumn('code'); 80 | table.dropColumn('country_id'); 81 | }); 82 | 83 | await knex.schema.table(tableNames.country, (table) => { 84 | table.dropColumn('code'); 85 | }); 86 | 87 | await Promise.all( 88 | [ 89 | tableNames.size, 90 | tableNames.item, 91 | tableNames.item_info, 92 | tableNames.item_image, 93 | tableNames.related_item, 94 | ] 95 | .reverse() 96 | .map((name) => knex.schema.dropTableIfExists(name)) 97 | ); 98 | }; 99 | -------------------------------------------------------------------------------- /backend/db/seeds/00_initial.js: -------------------------------------------------------------------------------- 1 | const crypto = require('crypto'); 2 | const bcrypt = require('bcrypt'); 3 | 4 | const logger = require('../../src/lib/logger'); 5 | const tableNames = require('../../src/constants/tableNames'); 6 | const countries = require('../../src/constants/countries'); 7 | const us_states = require('../../src/constants/us_states'); 8 | 9 | /** 10 | * @param {import('knex')} knex 11 | */ 12 | exports.seed = async (knex) => { 13 | await Promise.all(Object.keys(tableNames).map((name) => knex(name).del())); 14 | 15 | const password = `A${crypto.randomBytes(15).toString('hex')}?`; 16 | 17 | const user = { 18 | email: 'cj@null.computer', 19 | name: 'CJ', 20 | password: await bcrypt.hash(password, 12), 21 | }; 22 | 23 | const [createdUser] = await knex(tableNames.user).insert(user).returning('*'); 24 | 25 | if (process.env.NODE_ENV !== 'test') { 26 | logger.info( 27 | 'User created:', 28 | { 29 | password, 30 | }, 31 | createdUser 32 | ); 33 | } 34 | 35 | const insertedCountries = await knex(tableNames.country).insert( 36 | countries, 37 | '*' 38 | ); 39 | 40 | const usa = insertedCountries.find((country) => country.code === 'US'); 41 | 42 | us_states.forEach((state) => { 43 | state.country_id = usa.id; 44 | }); 45 | 46 | await knex(tableNames.state).insert(us_states); 47 | }; 48 | -------------------------------------------------------------------------------- /backend/db/seeds/01_initial_beans.js: -------------------------------------------------------------------------------- 1 | const tableNames = require('../../src/constants/tableNames'); 2 | 3 | /** 4 | * @param {import('knex')} knex 5 | */ 6 | exports.seed = async (knex) => { 7 | const [tennesee, usa] = await Promise.all([ 8 | knex(tableNames.state) 9 | .where({ 10 | code: 'TN', 11 | }) 12 | .first(), 13 | knex(tableNames.country) 14 | .where({ 15 | code: 'US', 16 | }) 17 | .first(), 18 | ]); 19 | 20 | const [address] = await knex(tableNames.address) 21 | .insert({ 22 | street_address_1: 'p.o. box 52330', 23 | street_address_2: 'dept. c', 24 | city: 'knoxville', 25 | zipcode: '37950-2330', 26 | latitude: 35.9603948, 27 | longitude: -83.9210261, 28 | state_id: tennesee.id, 29 | country_id: usa.id, 30 | }) 31 | .returning('id'); 32 | 33 | await knex(tableNames.company).insert({ 34 | name: 'Bush Brothers & Company', 35 | logo_url: 'https://i.imgur.com/KRKQ7LZ.jpg', 36 | description: 37 | "Bush Brothers and Company is a family-owned corporation best known for its Bush's Best brand canned baked beans.", 38 | website_url: 'https://bushbeans.com/', 39 | email: 'help@bushbros.com', 40 | address_id: address.id, 41 | }); 42 | 43 | await knex(tableNames.item_type).insert({ 44 | name: 'Canned Goods', 45 | }); 46 | 47 | await knex(tableNames.inventory_location).insert([ 48 | { 49 | name: 'Kitchen Pantry', 50 | }, 51 | { 52 | name: 'Basement Pantry', 53 | }, 54 | { 55 | name: 'Basement Freezer', 56 | }, 57 | { 58 | name: 'Kitchen Fridge', 59 | }, 60 | ]); 61 | }; 62 | -------------------------------------------------------------------------------- /backend/db/sources/countries.csv: -------------------------------------------------------------------------------- 1 | name,alpha-2,alpha-3,country-code,iso_3166-2,region,sub-region,intermediate-region,region-code,sub-region-code,intermediate-region-code 2 | Afghanistan,AF,AFG,004,ISO 3166-2:AF,Asia,Southern Asia,"",142,034,"" 3 | Åland Islands,AX,ALA,248,ISO 3166-2:AX,Europe,Northern Europe,"",150,154,"" 4 | Albania,AL,ALB,008,ISO 3166-2:AL,Europe,Southern Europe,"",150,039,"" 5 | Algeria,DZ,DZA,012,ISO 3166-2:DZ,Africa,Northern Africa,"",002,015,"" 6 | American Samoa,AS,ASM,016,ISO 3166-2:AS,Oceania,Polynesia,"",009,061,"" 7 | Andorra,AD,AND,020,ISO 3166-2:AD,Europe,Southern Europe,"",150,039,"" 8 | Angola,AO,AGO,024,ISO 3166-2:AO,Africa,Sub-Saharan Africa,Middle Africa,002,202,017 9 | Anguilla,AI,AIA,660,ISO 3166-2:AI,Americas,Latin America and the Caribbean,Caribbean,019,419,029 10 | Antarctica,AQ,ATA,010,ISO 3166-2:AQ,"","","","","","" 11 | Antigua and Barbuda,AG,ATG,028,ISO 3166-2:AG,Americas,Latin America and the Caribbean,Caribbean,019,419,029 12 | Argentina,AR,ARG,032,ISO 3166-2:AR,Americas,Latin America and the Caribbean,South America,019,419,005 13 | Armenia,AM,ARM,051,ISO 3166-2:AM,Asia,Western Asia,"",142,145,"" 14 | Aruba,AW,ABW,533,ISO 3166-2:AW,Americas,Latin America and the Caribbean,Caribbean,019,419,029 15 | Australia,AU,AUS,036,ISO 3166-2:AU,Oceania,Australia and New Zealand,"",009,053,"" 16 | Austria,AT,AUT,040,ISO 3166-2:AT,Europe,Western Europe,"",150,155,"" 17 | Azerbaijan,AZ,AZE,031,ISO 3166-2:AZ,Asia,Western Asia,"",142,145,"" 18 | Bahamas,BS,BHS,044,ISO 3166-2:BS,Americas,Latin America and the Caribbean,Caribbean,019,419,029 19 | Bahrain,BH,BHR,048,ISO 3166-2:BH,Asia,Western Asia,"",142,145,"" 20 | Bangladesh,BD,BGD,050,ISO 3166-2:BD,Asia,Southern Asia,"",142,034,"" 21 | Barbados,BB,BRB,052,ISO 3166-2:BB,Americas,Latin America and the Caribbean,Caribbean,019,419,029 22 | Belarus,BY,BLR,112,ISO 3166-2:BY,Europe,Eastern Europe,"",150,151,"" 23 | Belgium,BE,BEL,056,ISO 3166-2:BE,Europe,Western Europe,"",150,155,"" 24 | Belize,BZ,BLZ,084,ISO 3166-2:BZ,Americas,Latin America and the Caribbean,Central America,019,419,013 25 | Benin,BJ,BEN,204,ISO 3166-2:BJ,Africa,Sub-Saharan Africa,Western Africa,002,202,011 26 | Bermuda,BM,BMU,060,ISO 3166-2:BM,Americas,Northern America,"",019,021,"" 27 | Bhutan,BT,BTN,064,ISO 3166-2:BT,Asia,Southern Asia,"",142,034,"" 28 | Bolivia (Plurinational State of),BO,BOL,068,ISO 3166-2:BO,Americas,Latin America and the Caribbean,South America,019,419,005 29 | "Bonaire, Sint Eustatius and Saba",BQ,BES,535,ISO 3166-2:BQ,Americas,Latin America and the Caribbean,Caribbean,019,419,029 30 | Bosnia and Herzegovina,BA,BIH,070,ISO 3166-2:BA,Europe,Southern Europe,"",150,039,"" 31 | Botswana,BW,BWA,072,ISO 3166-2:BW,Africa,Sub-Saharan Africa,Southern Africa,002,202,018 32 | Bouvet Island,BV,BVT,074,ISO 3166-2:BV,Americas,Latin America and the Caribbean,South America,019,419,005 33 | Brazil,BR,BRA,076,ISO 3166-2:BR,Americas,Latin America and the Caribbean,South America,019,419,005 34 | British Indian Ocean Territory,IO,IOT,086,ISO 3166-2:IO,Africa,Sub-Saharan Africa,Eastern Africa,002,202,014 35 | Brunei Darussalam,BN,BRN,096,ISO 3166-2:BN,Asia,South-eastern Asia,"",142,035,"" 36 | Bulgaria,BG,BGR,100,ISO 3166-2:BG,Europe,Eastern Europe,"",150,151,"" 37 | Burkina Faso,BF,BFA,854,ISO 3166-2:BF,Africa,Sub-Saharan Africa,Western Africa,002,202,011 38 | Burundi,BI,BDI,108,ISO 3166-2:BI,Africa,Sub-Saharan Africa,Eastern Africa,002,202,014 39 | Cabo Verde,CV,CPV,132,ISO 3166-2:CV,Africa,Sub-Saharan Africa,Western Africa,002,202,011 40 | Cambodia,KH,KHM,116,ISO 3166-2:KH,Asia,South-eastern Asia,"",142,035,"" 41 | Cameroon,CM,CMR,120,ISO 3166-2:CM,Africa,Sub-Saharan Africa,Middle Africa,002,202,017 42 | Canada,CA,CAN,124,ISO 3166-2:CA,Americas,Northern America,"",019,021,"" 43 | Cayman Islands,KY,CYM,136,ISO 3166-2:KY,Americas,Latin America and the Caribbean,Caribbean,019,419,029 44 | Central African Republic,CF,CAF,140,ISO 3166-2:CF,Africa,Sub-Saharan Africa,Middle Africa,002,202,017 45 | Chad,TD,TCD,148,ISO 3166-2:TD,Africa,Sub-Saharan Africa,Middle Africa,002,202,017 46 | Chile,CL,CHL,152,ISO 3166-2:CL,Americas,Latin America and the Caribbean,South America,019,419,005 47 | China,CN,CHN,156,ISO 3166-2:CN,Asia,Eastern Asia,"",142,030,"" 48 | Christmas Island,CX,CXR,162,ISO 3166-2:CX,Oceania,Australia and New Zealand,"",009,053,"" 49 | Cocos (Keeling) Islands,CC,CCK,166,ISO 3166-2:CC,Oceania,Australia and New Zealand,"",009,053,"" 50 | Colombia,CO,COL,170,ISO 3166-2:CO,Americas,Latin America and the Caribbean,South America,019,419,005 51 | Comoros,KM,COM,174,ISO 3166-2:KM,Africa,Sub-Saharan Africa,Eastern Africa,002,202,014 52 | Congo,CG,COG,178,ISO 3166-2:CG,Africa,Sub-Saharan Africa,Middle Africa,002,202,017 53 | "Congo, Democratic Republic of the",CD,COD,180,ISO 3166-2:CD,Africa,Sub-Saharan Africa,Middle Africa,002,202,017 54 | Cook Islands,CK,COK,184,ISO 3166-2:CK,Oceania,Polynesia,"",009,061,"" 55 | Costa Rica,CR,CRI,188,ISO 3166-2:CR,Americas,Latin America and the Caribbean,Central America,019,419,013 56 | Côte d'Ivoire,CI,CIV,384,ISO 3166-2:CI,Africa,Sub-Saharan Africa,Western Africa,002,202,011 57 | Croatia,HR,HRV,191,ISO 3166-2:HR,Europe,Southern Europe,"",150,039,"" 58 | Cuba,CU,CUB,192,ISO 3166-2:CU,Americas,Latin America and the Caribbean,Caribbean,019,419,029 59 | Curaçao,CW,CUW,531,ISO 3166-2:CW,Americas,Latin America and the Caribbean,Caribbean,019,419,029 60 | Cyprus,CY,CYP,196,ISO 3166-2:CY,Asia,Western Asia,"",142,145,"" 61 | Czechia,CZ,CZE,203,ISO 3166-2:CZ,Europe,Eastern Europe,"",150,151,"" 62 | Denmark,DK,DNK,208,ISO 3166-2:DK,Europe,Northern Europe,"",150,154,"" 63 | Djibouti,DJ,DJI,262,ISO 3166-2:DJ,Africa,Sub-Saharan Africa,Eastern Africa,002,202,014 64 | Dominica,DM,DMA,212,ISO 3166-2:DM,Americas,Latin America and the Caribbean,Caribbean,019,419,029 65 | Dominican Republic,DO,DOM,214,ISO 3166-2:DO,Americas,Latin America and the Caribbean,Caribbean,019,419,029 66 | Ecuador,EC,ECU,218,ISO 3166-2:EC,Americas,Latin America and the Caribbean,South America,019,419,005 67 | Egypt,EG,EGY,818,ISO 3166-2:EG,Africa,Northern Africa,"",002,015,"" 68 | El Salvador,SV,SLV,222,ISO 3166-2:SV,Americas,Latin America and the Caribbean,Central America,019,419,013 69 | Equatorial Guinea,GQ,GNQ,226,ISO 3166-2:GQ,Africa,Sub-Saharan Africa,Middle Africa,002,202,017 70 | Eritrea,ER,ERI,232,ISO 3166-2:ER,Africa,Sub-Saharan Africa,Eastern Africa,002,202,014 71 | Estonia,EE,EST,233,ISO 3166-2:EE,Europe,Northern Europe,"",150,154,"" 72 | Eswatini,SZ,SWZ,748,ISO 3166-2:SZ,Africa,Sub-Saharan Africa,Southern Africa,002,202,018 73 | Ethiopia,ET,ETH,231,ISO 3166-2:ET,Africa,Sub-Saharan Africa,Eastern Africa,002,202,014 74 | Falkland Islands (Malvinas),FK,FLK,238,ISO 3166-2:FK,Americas,Latin America and the Caribbean,South America,019,419,005 75 | Faroe Islands,FO,FRO,234,ISO 3166-2:FO,Europe,Northern Europe,"",150,154,"" 76 | Fiji,FJ,FJI,242,ISO 3166-2:FJ,Oceania,Melanesia,"",009,054,"" 77 | Finland,FI,FIN,246,ISO 3166-2:FI,Europe,Northern Europe,"",150,154,"" 78 | France,FR,FRA,250,ISO 3166-2:FR,Europe,Western Europe,"",150,155,"" 79 | French Guiana,GF,GUF,254,ISO 3166-2:GF,Americas,Latin America and the Caribbean,South America,019,419,005 80 | French Polynesia,PF,PYF,258,ISO 3166-2:PF,Oceania,Polynesia,"",009,061,"" 81 | French Southern Territories,TF,ATF,260,ISO 3166-2:TF,Africa,Sub-Saharan Africa,Eastern Africa,002,202,014 82 | Gabon,GA,GAB,266,ISO 3166-2:GA,Africa,Sub-Saharan Africa,Middle Africa,002,202,017 83 | Gambia,GM,GMB,270,ISO 3166-2:GM,Africa,Sub-Saharan Africa,Western Africa,002,202,011 84 | Georgia,GE,GEO,268,ISO 3166-2:GE,Asia,Western Asia,"",142,145,"" 85 | Germany,DE,DEU,276,ISO 3166-2:DE,Europe,Western Europe,"",150,155,"" 86 | Ghana,GH,GHA,288,ISO 3166-2:GH,Africa,Sub-Saharan Africa,Western Africa,002,202,011 87 | Gibraltar,GI,GIB,292,ISO 3166-2:GI,Europe,Southern Europe,"",150,039,"" 88 | Greece,GR,GRC,300,ISO 3166-2:GR,Europe,Southern Europe,"",150,039,"" 89 | Greenland,GL,GRL,304,ISO 3166-2:GL,Americas,Northern America,"",019,021,"" 90 | Grenada,GD,GRD,308,ISO 3166-2:GD,Americas,Latin America and the Caribbean,Caribbean,019,419,029 91 | Guadeloupe,GP,GLP,312,ISO 3166-2:GP,Americas,Latin America and the Caribbean,Caribbean,019,419,029 92 | Guam,GU,GUM,316,ISO 3166-2:GU,Oceania,Micronesia,"",009,057,"" 93 | Guatemala,GT,GTM,320,ISO 3166-2:GT,Americas,Latin America and the Caribbean,Central America,019,419,013 94 | Guernsey,GG,GGY,831,ISO 3166-2:GG,Europe,Northern Europe,Channel Islands,150,154,830 95 | Guinea,GN,GIN,324,ISO 3166-2:GN,Africa,Sub-Saharan Africa,Western Africa,002,202,011 96 | Guinea-Bissau,GW,GNB,624,ISO 3166-2:GW,Africa,Sub-Saharan Africa,Western Africa,002,202,011 97 | Guyana,GY,GUY,328,ISO 3166-2:GY,Americas,Latin America and the Caribbean,South America,019,419,005 98 | Haiti,HT,HTI,332,ISO 3166-2:HT,Americas,Latin America and the Caribbean,Caribbean,019,419,029 99 | Heard Island and McDonald Islands,HM,HMD,334,ISO 3166-2:HM,Oceania,Australia and New Zealand,"",009,053,"" 100 | Holy See,VA,VAT,336,ISO 3166-2:VA,Europe,Southern Europe,"",150,039,"" 101 | Honduras,HN,HND,340,ISO 3166-2:HN,Americas,Latin America and the Caribbean,Central America,019,419,013 102 | Hong Kong,HK,HKG,344,ISO 3166-2:HK,Asia,Eastern Asia,"",142,030,"" 103 | Hungary,HU,HUN,348,ISO 3166-2:HU,Europe,Eastern Europe,"",150,151,"" 104 | Iceland,IS,ISL,352,ISO 3166-2:IS,Europe,Northern Europe,"",150,154,"" 105 | India,IN,IND,356,ISO 3166-2:IN,Asia,Southern Asia,"",142,034,"" 106 | Indonesia,ID,IDN,360,ISO 3166-2:ID,Asia,South-eastern Asia,"",142,035,"" 107 | Iran (Islamic Republic of),IR,IRN,364,ISO 3166-2:IR,Asia,Southern Asia,"",142,034,"" 108 | Iraq,IQ,IRQ,368,ISO 3166-2:IQ,Asia,Western Asia,"",142,145,"" 109 | Ireland,IE,IRL,372,ISO 3166-2:IE,Europe,Northern Europe,"",150,154,"" 110 | Isle of Man,IM,IMN,833,ISO 3166-2:IM,Europe,Northern Europe,"",150,154,"" 111 | Israel,IL,ISR,376,ISO 3166-2:IL,Asia,Western Asia,"",142,145,"" 112 | Italy,IT,ITA,380,ISO 3166-2:IT,Europe,Southern Europe,"",150,039,"" 113 | Jamaica,JM,JAM,388,ISO 3166-2:JM,Americas,Latin America and the Caribbean,Caribbean,019,419,029 114 | Japan,JP,JPN,392,ISO 3166-2:JP,Asia,Eastern Asia,"",142,030,"" 115 | Jersey,JE,JEY,832,ISO 3166-2:JE,Europe,Northern Europe,Channel Islands,150,154,830 116 | Jordan,JO,JOR,400,ISO 3166-2:JO,Asia,Western Asia,"",142,145,"" 117 | Kazakhstan,KZ,KAZ,398,ISO 3166-2:KZ,Asia,Central Asia,"",142,143,"" 118 | Kenya,KE,KEN,404,ISO 3166-2:KE,Africa,Sub-Saharan Africa,Eastern Africa,002,202,014 119 | Kiribati,KI,KIR,296,ISO 3166-2:KI,Oceania,Micronesia,"",009,057,"" 120 | Korea (Democratic People's Republic of),KP,PRK,408,ISO 3166-2:KP,Asia,Eastern Asia,"",142,030,"" 121 | "Korea, Republic of",KR,KOR,410,ISO 3166-2:KR,Asia,Eastern Asia,"",142,030,"" 122 | Kuwait,KW,KWT,414,ISO 3166-2:KW,Asia,Western Asia,"",142,145,"" 123 | Kyrgyzstan,KG,KGZ,417,ISO 3166-2:KG,Asia,Central Asia,"",142,143,"" 124 | Lao People's Democratic Republic,LA,LAO,418,ISO 3166-2:LA,Asia,South-eastern Asia,"",142,035,"" 125 | Latvia,LV,LVA,428,ISO 3166-2:LV,Europe,Northern Europe,"",150,154,"" 126 | Lebanon,LB,LBN,422,ISO 3166-2:LB,Asia,Western Asia,"",142,145,"" 127 | Lesotho,LS,LSO,426,ISO 3166-2:LS,Africa,Sub-Saharan Africa,Southern Africa,002,202,018 128 | Liberia,LR,LBR,430,ISO 3166-2:LR,Africa,Sub-Saharan Africa,Western Africa,002,202,011 129 | Libya,LY,LBY,434,ISO 3166-2:LY,Africa,Northern Africa,"",002,015,"" 130 | Liechtenstein,LI,LIE,438,ISO 3166-2:LI,Europe,Western Europe,"",150,155,"" 131 | Lithuania,LT,LTU,440,ISO 3166-2:LT,Europe,Northern Europe,"",150,154,"" 132 | Luxembourg,LU,LUX,442,ISO 3166-2:LU,Europe,Western Europe,"",150,155,"" 133 | Macao,MO,MAC,446,ISO 3166-2:MO,Asia,Eastern Asia,"",142,030,"" 134 | Madagascar,MG,MDG,450,ISO 3166-2:MG,Africa,Sub-Saharan Africa,Eastern Africa,002,202,014 135 | Malawi,MW,MWI,454,ISO 3166-2:MW,Africa,Sub-Saharan Africa,Eastern Africa,002,202,014 136 | Malaysia,MY,MYS,458,ISO 3166-2:MY,Asia,South-eastern Asia,"",142,035,"" 137 | Maldives,MV,MDV,462,ISO 3166-2:MV,Asia,Southern Asia,"",142,034,"" 138 | Mali,ML,MLI,466,ISO 3166-2:ML,Africa,Sub-Saharan Africa,Western Africa,002,202,011 139 | Malta,MT,MLT,470,ISO 3166-2:MT,Europe,Southern Europe,"",150,039,"" 140 | Marshall Islands,MH,MHL,584,ISO 3166-2:MH,Oceania,Micronesia,"",009,057,"" 141 | Martinique,MQ,MTQ,474,ISO 3166-2:MQ,Americas,Latin America and the Caribbean,Caribbean,019,419,029 142 | Mauritania,MR,MRT,478,ISO 3166-2:MR,Africa,Sub-Saharan Africa,Western Africa,002,202,011 143 | Mauritius,MU,MUS,480,ISO 3166-2:MU,Africa,Sub-Saharan Africa,Eastern Africa,002,202,014 144 | Mayotte,YT,MYT,175,ISO 3166-2:YT,Africa,Sub-Saharan Africa,Eastern Africa,002,202,014 145 | Mexico,MX,MEX,484,ISO 3166-2:MX,Americas,Latin America and the Caribbean,Central America,019,419,013 146 | Micronesia (Federated States of),FM,FSM,583,ISO 3166-2:FM,Oceania,Micronesia,"",009,057,"" 147 | "Moldova, Republic of",MD,MDA,498,ISO 3166-2:MD,Europe,Eastern Europe,"",150,151,"" 148 | Monaco,MC,MCO,492,ISO 3166-2:MC,Europe,Western Europe,"",150,155,"" 149 | Mongolia,MN,MNG,496,ISO 3166-2:MN,Asia,Eastern Asia,"",142,030,"" 150 | Montenegro,ME,MNE,499,ISO 3166-2:ME,Europe,Southern Europe,"",150,039,"" 151 | Montserrat,MS,MSR,500,ISO 3166-2:MS,Americas,Latin America and the Caribbean,Caribbean,019,419,029 152 | Morocco,MA,MAR,504,ISO 3166-2:MA,Africa,Northern Africa,"",002,015,"" 153 | Mozambique,MZ,MOZ,508,ISO 3166-2:MZ,Africa,Sub-Saharan Africa,Eastern Africa,002,202,014 154 | Myanmar,MM,MMR,104,ISO 3166-2:MM,Asia,South-eastern Asia,"",142,035,"" 155 | Namibia,NA,NAM,516,ISO 3166-2:NA,Africa,Sub-Saharan Africa,Southern Africa,002,202,018 156 | Nauru,NR,NRU,520,ISO 3166-2:NR,Oceania,Micronesia,"",009,057,"" 157 | Nepal,NP,NPL,524,ISO 3166-2:NP,Asia,Southern Asia,"",142,034,"" 158 | Netherlands,NL,NLD,528,ISO 3166-2:NL,Europe,Western Europe,"",150,155,"" 159 | New Caledonia,NC,NCL,540,ISO 3166-2:NC,Oceania,Melanesia,"",009,054,"" 160 | New Zealand,NZ,NZL,554,ISO 3166-2:NZ,Oceania,Australia and New Zealand,"",009,053,"" 161 | Nicaragua,NI,NIC,558,ISO 3166-2:NI,Americas,Latin America and the Caribbean,Central America,019,419,013 162 | Niger,NE,NER,562,ISO 3166-2:NE,Africa,Sub-Saharan Africa,Western Africa,002,202,011 163 | Nigeria,NG,NGA,566,ISO 3166-2:NG,Africa,Sub-Saharan Africa,Western Africa,002,202,011 164 | Niue,NU,NIU,570,ISO 3166-2:NU,Oceania,Polynesia,"",009,061,"" 165 | Norfolk Island,NF,NFK,574,ISO 3166-2:NF,Oceania,Australia and New Zealand,"",009,053,"" 166 | North Macedonia,MK,MKD,807,ISO 3166-2:MK,Europe,Southern Europe,"",150,039,"" 167 | Northern Mariana Islands,MP,MNP,580,ISO 3166-2:MP,Oceania,Micronesia,"",009,057,"" 168 | Norway,NO,NOR,578,ISO 3166-2:NO,Europe,Northern Europe,"",150,154,"" 169 | Oman,OM,OMN,512,ISO 3166-2:OM,Asia,Western Asia,"",142,145,"" 170 | Pakistan,PK,PAK,586,ISO 3166-2:PK,Asia,Southern Asia,"",142,034,"" 171 | Palau,PW,PLW,585,ISO 3166-2:PW,Oceania,Micronesia,"",009,057,"" 172 | "Palestine, State of",PS,PSE,275,ISO 3166-2:PS,Asia,Western Asia,"",142,145,"" 173 | Panama,PA,PAN,591,ISO 3166-2:PA,Americas,Latin America and the Caribbean,Central America,019,419,013 174 | Papua New Guinea,PG,PNG,598,ISO 3166-2:PG,Oceania,Melanesia,"",009,054,"" 175 | Paraguay,PY,PRY,600,ISO 3166-2:PY,Americas,Latin America and the Caribbean,South America,019,419,005 176 | Peru,PE,PER,604,ISO 3166-2:PE,Americas,Latin America and the Caribbean,South America,019,419,005 177 | Philippines,PH,PHL,608,ISO 3166-2:PH,Asia,South-eastern Asia,"",142,035,"" 178 | Pitcairn,PN,PCN,612,ISO 3166-2:PN,Oceania,Polynesia,"",009,061,"" 179 | Poland,PL,POL,616,ISO 3166-2:PL,Europe,Eastern Europe,"",150,151,"" 180 | Portugal,PT,PRT,620,ISO 3166-2:PT,Europe,Southern Europe,"",150,039,"" 181 | Puerto Rico,PR,PRI,630,ISO 3166-2:PR,Americas,Latin America and the Caribbean,Caribbean,019,419,029 182 | Qatar,QA,QAT,634,ISO 3166-2:QA,Asia,Western Asia,"",142,145,"" 183 | Réunion,RE,REU,638,ISO 3166-2:RE,Africa,Sub-Saharan Africa,Eastern Africa,002,202,014 184 | Romania,RO,ROU,642,ISO 3166-2:RO,Europe,Eastern Europe,"",150,151,"" 185 | Russian Federation,RU,RUS,643,ISO 3166-2:RU,Europe,Eastern Europe,"",150,151,"" 186 | Rwanda,RW,RWA,646,ISO 3166-2:RW,Africa,Sub-Saharan Africa,Eastern Africa,002,202,014 187 | Saint Barthélemy,BL,BLM,652,ISO 3166-2:BL,Americas,Latin America and the Caribbean,Caribbean,019,419,029 188 | "Saint Helena, Ascension and Tristan da Cunha",SH,SHN,654,ISO 3166-2:SH,Africa,Sub-Saharan Africa,Western Africa,002,202,011 189 | Saint Kitts and Nevis,KN,KNA,659,ISO 3166-2:KN,Americas,Latin America and the Caribbean,Caribbean,019,419,029 190 | Saint Lucia,LC,LCA,662,ISO 3166-2:LC,Americas,Latin America and the Caribbean,Caribbean,019,419,029 191 | Saint Martin (French part),MF,MAF,663,ISO 3166-2:MF,Americas,Latin America and the Caribbean,Caribbean,019,419,029 192 | Saint Pierre and Miquelon,PM,SPM,666,ISO 3166-2:PM,Americas,Northern America,"",019,021,"" 193 | Saint Vincent and the Grenadines,VC,VCT,670,ISO 3166-2:VC,Americas,Latin America and the Caribbean,Caribbean,019,419,029 194 | Samoa,WS,WSM,882,ISO 3166-2:WS,Oceania,Polynesia,"",009,061,"" 195 | San Marino,SM,SMR,674,ISO 3166-2:SM,Europe,Southern Europe,"",150,039,"" 196 | Sao Tome and Principe,ST,STP,678,ISO 3166-2:ST,Africa,Sub-Saharan Africa,Middle Africa,002,202,017 197 | Saudi Arabia,SA,SAU,682,ISO 3166-2:SA,Asia,Western Asia,"",142,145,"" 198 | Senegal,SN,SEN,686,ISO 3166-2:SN,Africa,Sub-Saharan Africa,Western Africa,002,202,011 199 | Serbia,RS,SRB,688,ISO 3166-2:RS,Europe,Southern Europe,"",150,039,"" 200 | Seychelles,SC,SYC,690,ISO 3166-2:SC,Africa,Sub-Saharan Africa,Eastern Africa,002,202,014 201 | Sierra Leone,SL,SLE,694,ISO 3166-2:SL,Africa,Sub-Saharan Africa,Western Africa,002,202,011 202 | Singapore,SG,SGP,702,ISO 3166-2:SG,Asia,South-eastern Asia,"",142,035,"" 203 | Sint Maarten (Dutch part),SX,SXM,534,ISO 3166-2:SX,Americas,Latin America and the Caribbean,Caribbean,019,419,029 204 | Slovakia,SK,SVK,703,ISO 3166-2:SK,Europe,Eastern Europe,"",150,151,"" 205 | Slovenia,SI,SVN,705,ISO 3166-2:SI,Europe,Southern Europe,"",150,039,"" 206 | Solomon Islands,SB,SLB,090,ISO 3166-2:SB,Oceania,Melanesia,"",009,054,"" 207 | Somalia,SO,SOM,706,ISO 3166-2:SO,Africa,Sub-Saharan Africa,Eastern Africa,002,202,014 208 | South Africa,ZA,ZAF,710,ISO 3166-2:ZA,Africa,Sub-Saharan Africa,Southern Africa,002,202,018 209 | South Georgia and the South Sandwich Islands,GS,SGS,239,ISO 3166-2:GS,Americas,Latin America and the Caribbean,South America,019,419,005 210 | South Sudan,SS,SSD,728,ISO 3166-2:SS,Africa,Sub-Saharan Africa,Eastern Africa,002,202,014 211 | Spain,ES,ESP,724,ISO 3166-2:ES,Europe,Southern Europe,"",150,039,"" 212 | Sri Lanka,LK,LKA,144,ISO 3166-2:LK,Asia,Southern Asia,"",142,034,"" 213 | Sudan,SD,SDN,729,ISO 3166-2:SD,Africa,Northern Africa,"",002,015,"" 214 | Suriname,SR,SUR,740,ISO 3166-2:SR,Americas,Latin America and the Caribbean,South America,019,419,005 215 | Svalbard and Jan Mayen,SJ,SJM,744,ISO 3166-2:SJ,Europe,Northern Europe,"",150,154,"" 216 | Sweden,SE,SWE,752,ISO 3166-2:SE,Europe,Northern Europe,"",150,154,"" 217 | Switzerland,CH,CHE,756,ISO 3166-2:CH,Europe,Western Europe,"",150,155,"" 218 | Syrian Arab Republic,SY,SYR,760,ISO 3166-2:SY,Asia,Western Asia,"",142,145,"" 219 | "Taiwan, Province of China",TW,TWN,158,ISO 3166-2:TW,Asia,Eastern Asia,"",142,030,"" 220 | Tajikistan,TJ,TJK,762,ISO 3166-2:TJ,Asia,Central Asia,"",142,143,"" 221 | "Tanzania, United Republic of",TZ,TZA,834,ISO 3166-2:TZ,Africa,Sub-Saharan Africa,Eastern Africa,002,202,014 222 | Thailand,TH,THA,764,ISO 3166-2:TH,Asia,South-eastern Asia,"",142,035,"" 223 | Timor-Leste,TL,TLS,626,ISO 3166-2:TL,Asia,South-eastern Asia,"",142,035,"" 224 | Togo,TG,TGO,768,ISO 3166-2:TG,Africa,Sub-Saharan Africa,Western Africa,002,202,011 225 | Tokelau,TK,TKL,772,ISO 3166-2:TK,Oceania,Polynesia,"",009,061,"" 226 | Tonga,TO,TON,776,ISO 3166-2:TO,Oceania,Polynesia,"",009,061,"" 227 | Trinidad and Tobago,TT,TTO,780,ISO 3166-2:TT,Americas,Latin America and the Caribbean,Caribbean,019,419,029 228 | Tunisia,TN,TUN,788,ISO 3166-2:TN,Africa,Northern Africa,"",002,015,"" 229 | Turkey,TR,TUR,792,ISO 3166-2:TR,Asia,Western Asia,"",142,145,"" 230 | Turkmenistan,TM,TKM,795,ISO 3166-2:TM,Asia,Central Asia,"",142,143,"" 231 | Turks and Caicos Islands,TC,TCA,796,ISO 3166-2:TC,Americas,Latin America and the Caribbean,Caribbean,019,419,029 232 | Tuvalu,TV,TUV,798,ISO 3166-2:TV,Oceania,Polynesia,"",009,061,"" 233 | Uganda,UG,UGA,800,ISO 3166-2:UG,Africa,Sub-Saharan Africa,Eastern Africa,002,202,014 234 | Ukraine,UA,UKR,804,ISO 3166-2:UA,Europe,Eastern Europe,"",150,151,"" 235 | United Arab Emirates,AE,ARE,784,ISO 3166-2:AE,Asia,Western Asia,"",142,145,"" 236 | United Kingdom of Great Britain and Northern Ireland,GB,GBR,826,ISO 3166-2:GB,Europe,Northern Europe,"",150,154,"" 237 | United States of America,US,USA,840,ISO 3166-2:US,Americas,Northern America,"",019,021,"" 238 | United States Minor Outlying Islands,UM,UMI,581,ISO 3166-2:UM,Oceania,Micronesia,"",009,057,"" 239 | Uruguay,UY,URY,858,ISO 3166-2:UY,Americas,Latin America and the Caribbean,South America,019,419,005 240 | Uzbekistan,UZ,UZB,860,ISO 3166-2:UZ,Asia,Central Asia,"",142,143,"" 241 | Vanuatu,VU,VUT,548,ISO 3166-2:VU,Oceania,Melanesia,"",009,054,"" 242 | Venezuela (Bolivarian Republic of),VE,VEN,862,ISO 3166-2:VE,Americas,Latin America and the Caribbean,South America,019,419,005 243 | Viet Nam,VN,VNM,704,ISO 3166-2:VN,Asia,South-eastern Asia,"",142,035,"" 244 | Virgin Islands (British),VG,VGB,092,ISO 3166-2:VG,Americas,Latin America and the Caribbean,Caribbean,019,419,029 245 | Virgin Islands (U.S.),VI,VIR,850,ISO 3166-2:VI,Americas,Latin America and the Caribbean,Caribbean,019,419,029 246 | Wallis and Futuna,WF,WLF,876,ISO 3166-2:WF,Oceania,Polynesia,"",009,061,"" 247 | Western Sahara,EH,ESH,732,ISO 3166-2:EH,Africa,Northern Africa,"",002,015,"" 248 | Yemen,YE,YEM,887,ISO 3166-2:YE,Asia,Western Asia,"",142,145,"" 249 | Zambia,ZM,ZMB,894,ISO 3166-2:ZM,Africa,Sub-Saharan Africa,Eastern Africa,002,202,014 250 | Zimbabwe,ZW,ZWE,716,ISO 3166-2:ZW,Africa,Sub-Saharan Africa,Eastern Africa,002,202,014 251 | -------------------------------------------------------------------------------- /backend/db/sources/us_states.csv: -------------------------------------------------------------------------------- 1 | "state","slug","code","nickname","website","admission_date","admission_number","capital_city","capital_url","population","population_rank","constitution_url","state_flag_url","state_seal_url","map_image_url","landscape_background_url","skyline_background_url","twitter_url","facebook_url" 2 | "Alabama","alabama","AL","Yellowhammer State","http://www.alabama.gov","1819-12-14",22,"Montgomery","http://www.montgomeryal.gov",4833722,23,"http://alisondb.legislature.state.al.us/alison/default.aspx","https://cdn.civil.services/us-states/flags/alabama-large.png","https://cdn.civil.services/us-states/seals/alabama-large.png","https://cdn.civil.services/us-states/maps/alabama-large.png","https://cdn.civil.services/us-states/backgrounds/1280x720/landscape/alabama.jpg","https://cdn.civil.services/us-states/backgrounds/1280x720/skyline/alabama.jpg","https://twitter.com/alabamagov","https://www.facebook.com/alabamagov" 3 | "Alaska","alaska","AK","The Last Frontier","http://alaska.gov","1959-01-03",49,"Juneau","http://www.juneau.org",735132,47,"http://www.legis.state.ak.us/basis/folioproxy.asp?url=http://wwwjnu01.legis.state.ak.us/cgi-bin/folioisa.dll/acontxt/query=*/doc/{t1}?","https://cdn.civil.services/us-states/flags/alaska-large.png","https://cdn.civil.services/us-states/seals/alaska-large.png","https://cdn.civil.services/us-states/maps/alaska-large.png","https://cdn.civil.services/us-states/backgrounds/1280x720/landscape/alaska.jpg","https://cdn.civil.services/us-states/backgrounds/1280x720/skyline/alaska.jpg","https://twitter.com/alaska","https://www.facebook.com/AlaskaLocalGovernments" 4 | "Arizona","arizona","AZ","The Grand Canyon State","https://az.gov","1912-02-14",48,"Phoenix","https://www.phoenix.gov",6626624,15,"http://www.azleg.gov/Constitution.asp","https://cdn.civil.services/us-states/flags/arizona-large.png","https://cdn.civil.services/us-states/seals/arizona-large.png","https://cdn.civil.services/us-states/maps/arizona-large.png","https://cdn.civil.services/us-states/backgrounds/1280x720/landscape/arizona.jpg","https://cdn.civil.services/us-states/backgrounds/1280x720/skyline/arizona.jpg",, 5 | "Arkansas","arkansas","AR","The Natural State","http://arkansas.gov","1836-06-15",25,"Little Rock","http://www.littlerock.org",2959373,32,"http://www.arkleg.state.ar.us/assembly/Summary/ArkansasConstitution1874.pdf","https://cdn.civil.services/us-states/flags/arkansas-large.png","https://cdn.civil.services/us-states/seals/arkansas-large.png","https://cdn.civil.services/us-states/maps/arkansas-large.png","https://cdn.civil.services/us-states/backgrounds/1280x720/landscape/arkansas.jpg","https://cdn.civil.services/us-states/backgrounds/1280x720/skyline/arkansas.jpg","https://twitter.com/arkansasgov","https://www.facebook.com/Arkansas.gov" 6 | "California","california","CA","Golden State","http://www.ca.gov","1850-09-09",31,"Sacramento","http://www.cityofsacramento.org",38332521,1,"http://www.leginfo.ca.gov/const-toc.html","https://cdn.civil.services/us-states/flags/california-large.png","https://cdn.civil.services/us-states/seals/california-large.png","https://cdn.civil.services/us-states/maps/california-large.png","https://cdn.civil.services/us-states/backgrounds/1280x720/landscape/california.jpg","https://cdn.civil.services/us-states/backgrounds/1280x720/skyline/california.jpg","https://twitter.com/cagovernment", 7 | "Colorado","colorado","CO","The Centennial State","https://www.colorado.gov","1876-08-01",38,"Denver","http://www.denvergov.org",5268367,22,"https://www.colorado.gov/pacific/archives/government","https://cdn.civil.services/us-states/flags/colorado-large.png","https://cdn.civil.services/us-states/seals/colorado-large.png","https://cdn.civil.services/us-states/maps/colorado-large.png","https://cdn.civil.services/us-states/backgrounds/1280x720/landscape/colorado.jpg","https://cdn.civil.services/us-states/backgrounds/1280x720/skyline/colorado.jpg","https://twitter.com/coloradogov","https://www.facebook.com/Colorado.gov" 8 | "Connecticut","connecticut","CT","Constitution State","http://www.ct.gov","1788-01-09",5,"Hartford","http://www.hartford.gov",3596080,29,"http://www.ct.gov/sots/cwp/view.asp?a=3188&q=392288","https://cdn.civil.services/us-states/flags/connecticut-large.png","https://cdn.civil.services/us-states/seals/connecticut-large.png","https://cdn.civil.services/us-states/maps/connecticut-large.png","https://cdn.civil.services/us-states/backgrounds/1280x720/landscape/connecticut.jpg","https://cdn.civil.services/us-states/backgrounds/1280x720/skyline/connecticut.jpg",, 9 | "Delaware","delaware","DE","The First State / The Diamond State","http://delaware.gov","1787-12-07",1,"Dover","http://www.cityofdover.com",925749,45,"http://www.state.de.us/facts/constit/welcome.htm","https://cdn.civil.services/us-states/flags/delaware-large.png","https://cdn.civil.services/us-states/seals/delaware-large.png","https://cdn.civil.services/us-states/maps/delaware-large.png","https://cdn.civil.services/us-states/backgrounds/1280x720/landscape/delaware.jpg","https://cdn.civil.services/us-states/backgrounds/1280x720/skyline/delaware.jpg","https://twitter.com/delaware_gov","https://www.facebook.com/delaware.gov" 10 | "Florida","florida","FL","Sunshine State","http://www.myflorida.com","1845-03-03",27,"Tallahassee","https://www.talgov.com/Main/Home.aspx",19552860,4,"http://www.leg.state.fl.us/Statutes/index.cfm","https://cdn.civil.services/us-states/flags/florida-large.png","https://cdn.civil.services/us-states/seals/florida-large.png","https://cdn.civil.services/us-states/maps/florida-large.png","https://cdn.civil.services/us-states/backgrounds/1280x720/landscape/florida.jpg","https://cdn.civil.services/us-states/backgrounds/1280x720/skyline/florida.jpg",, 11 | "Georgia","georgia","GA","Peach State","http://georgia.gov","1788-01-02",4,"Atlanta","http://www.atlantaga.gov",9992167,8,"http://sos.ga.gov/admin/files/Constitution_2013_Final_Printed.pdf","https://cdn.civil.services/us-states/flags/georgia-large.png","https://cdn.civil.services/us-states/seals/georgia-large.png","https://cdn.civil.services/us-states/maps/georgia-large.png","https://cdn.civil.services/us-states/backgrounds/1280x720/landscape/georgia.jpg","https://cdn.civil.services/us-states/backgrounds/1280x720/skyline/georgia.jpg","http://twitter.com/georgiagov","http://www.facebook.com/pages/georgiagov/29760668054" 12 | "Hawaii","hawaii","HI","Aloha State","https://www.ehawaii.gov","1959-08-21",50,"Honolulu","http://www.co.honolulu.hi.us",1404054,40,"http://lrbhawaii.org/con","https://cdn.civil.services/us-states/flags/hawaii-large.png","https://cdn.civil.services/us-states/seals/hawaii-large.png","https://cdn.civil.services/us-states/maps/hawaii-large.png","https://cdn.civil.services/us-states/backgrounds/1280x720/landscape/hawaii.jpg","https://cdn.civil.services/us-states/backgrounds/1280x720/skyline/hawaii.jpg","https://twitter.com/ehawaiigov","https://www.facebook.com/ehawaii.gov" 13 | "Idaho","idaho","ID","Gem State","https://www.idaho.gov","1890-07-03",43,"Boise","http://www.cityofboise.org",1612136,39,"http://www.legislature.idaho.gov/idstat/IC/Title003.htm","https://cdn.civil.services/us-states/flags/idaho-large.png","https://cdn.civil.services/us-states/seals/idaho-large.png","https://cdn.civil.services/us-states/maps/idaho-large.png","https://cdn.civil.services/us-states/backgrounds/1280x720/landscape/idaho.jpg","https://cdn.civil.services/us-states/backgrounds/1280x720/skyline/idaho.jpg","https://twitter.com/IDAHOgov", 14 | "Illinois","illinois","IL","Prairie State","https://www.illinois.gov","1818-12-03",21,"Springfield","http://www.springfield.il.us",12882135,5,"http://www.ilga.gov/commission/lrb/conmain.htm","https://cdn.civil.services/us-states/flags/illinois-large.png","https://cdn.civil.services/us-states/seals/illinois-large.png","https://cdn.civil.services/us-states/maps/illinois-large.png","https://cdn.civil.services/us-states/backgrounds/1280x720/landscape/illinois.jpg","https://cdn.civil.services/us-states/backgrounds/1280x720/skyline/illinois.jpg",, 15 | "Indiana","indiana","IN","Hoosier State","http://www.in.gov","1816-12-11",19,"Indianapolis","http://www.indy.gov/Pages/Home.aspx",6570902,16,"http://www.law.indiana.edu/uslawdocs/inconst.html","https://cdn.civil.services/us-states/flags/indiana-large.png","https://cdn.civil.services/us-states/seals/indiana-large.png","https://cdn.civil.services/us-states/maps/indiana-large.png","https://cdn.civil.services/us-states/backgrounds/1280x720/landscape/indiana.jpg","https://cdn.civil.services/us-states/backgrounds/1280x720/skyline/indiana.jpg","https://twitter.com/in_gov","https://www.facebook.com/IndianaGovernment" 16 | "Iowa","iowa","IA","Hawkeye State","https://www.iowa.gov","1846-12-28",29,"Des Moines","http://www.ci.des-moines.ia.us",3090416,30,"http://publications.iowa.gov/135/1/history/7-7.html","https://cdn.civil.services/us-states/flags/iowa-large.png","https://cdn.civil.services/us-states/seals/iowa-large.png","https://cdn.civil.services/us-states/maps/iowa-large.png","https://cdn.civil.services/us-states/backgrounds/1280x720/landscape/iowa.jpg","https://cdn.civil.services/us-states/backgrounds/1280x720/skyline/iowa.jpg","https://twitter.com/IAGOVTWEETS", 17 | "Kansas","kansas","KS","Sunflower State","https://www.kansas.gov","1861-01-29",34,"Topeka","http://www.topeka.org",2893957,34,"https://kslib.info/405/Kansas-Constitution","https://cdn.civil.services/us-states/flags/kansas-large.png","https://cdn.civil.services/us-states/seals/kansas-large.png","https://cdn.civil.services/us-states/maps/kansas-large.png","https://cdn.civil.services/us-states/backgrounds/1280x720/landscape/kansas.jpg","https://cdn.civil.services/us-states/backgrounds/1280x720/skyline/kansas.jpg","http://www.twitter.com/ksgovernment","http://www.facebook.com/pages/Topeka-KS/Kansasgov-Kansas-Government-Online/52068474220" 18 | "Kentucky","kentucky","KY","Bluegrass State","http://kentucky.gov","1792-06-01",15,"Frankfort","http://frankfort.ky.gov",4395295,26,"http://www.lrc.state.ky.us/Legresou/Constitu/intro.htm","https://cdn.civil.services/us-states/flags/kentucky-large.png","https://cdn.civil.services/us-states/seals/kentucky-large.png","https://cdn.civil.services/us-states/maps/kentucky-large.png","https://cdn.civil.services/us-states/backgrounds/1280x720/landscape/kentucky.jpg","https://cdn.civil.services/us-states/backgrounds/1280x720/skyline/kentucky.jpg","https://twitter.com/kygov","https://www.facebook.com/kygov" 19 | "Louisiana","louisiana","LA","Pelican State","http://louisiana.gov","1812-04-30",18,"Baton Rouge","http://brgov.com",4625470,25,"http://senate.legis.state.la.us/Documents/Constitution","https://cdn.civil.services/us-states/flags/louisiana-large.png","https://cdn.civil.services/us-states/seals/louisiana-large.png","https://cdn.civil.services/us-states/maps/louisiana-large.png","https://cdn.civil.services/us-states/backgrounds/1280x720/landscape/louisiana.jpg","https://cdn.civil.services/us-states/backgrounds/1280x720/skyline/louisiana.jpg",, 20 | "Maine","maine","ME","Pine Tree State","http://www.maine.gov","1820-03-15",23,"Augusta","http://www.augustamaine.gov",1328302,41,"http://www.maine.gov/legis/const","https://cdn.civil.services/us-states/flags/maine-large.png","https://cdn.civil.services/us-states/seals/maine-large.png","https://cdn.civil.services/us-states/maps/maine-large.png","https://cdn.civil.services/us-states/backgrounds/1280x720/landscape/maine.jpg","https://cdn.civil.services/us-states/backgrounds/1280x720/skyline/maine.jpg","https://twitter.com/mainegov_news","http://www.facebook.com/pages/Augusta-ME/Mainegov/98519328240" 21 | "Maryland","maryland","MD","Old Line State","http://www.maryland.gov","1788-04-28",7,"Annapolis","http://www.annapolis.gov",5928814,19,"http://msa.maryland.gov/msa/mdmanual/43const/html/const.html","https://cdn.civil.services/us-states/flags/maryland-large.png","https://cdn.civil.services/us-states/seals/maryland-large.png","https://cdn.civil.services/us-states/maps/maryland-large.png","https://cdn.civil.services/us-states/backgrounds/1280x720/landscape/maryland.jpg","https://cdn.civil.services/us-states/backgrounds/1280x720/skyline/maryland.jpg","https://twitter.com/statemaryland","https://www.facebook.com/statemaryland" 22 | "Massachusetts","massachusetts","MA","Bay State","http://www.mass.gov","1788-02-06",6,"Boston","http://www.ci.boston.ma.us",6692824,14,"http://www.state.ma.us/legis/const.htm","https://cdn.civil.services/us-states/flags/massachusetts-large.png","https://cdn.civil.services/us-states/seals/massachusetts-large.png","https://cdn.civil.services/us-states/maps/massachusetts-large.png","https://cdn.civil.services/us-states/backgrounds/1280x720/landscape/massachusetts.jpg","https://cdn.civil.services/us-states/backgrounds/1280x720/skyline/massachusetts.jpg","http://twitter.com/massgov","https://www.facebook.com/massgov" 23 | "Michigan","michigan","MI","Wolverine State / Great Lakes State","http://www.michigan.gov","1837-01-26",26,"Lansing","http://cityoflansingmi.com",9895622,9,"http://www.legislature.mi.gov/(S(hrowl12tg05hemnnkidim1jb))/mileg.aspx?page=GetObject&objectname=mcl-Constitution","https://cdn.civil.services/us-states/flags/michigan-large.png","https://cdn.civil.services/us-states/seals/michigan-large.png","https://cdn.civil.services/us-states/maps/michigan-large.png","https://cdn.civil.services/us-states/backgrounds/1280x720/landscape/michigan.jpg","https://cdn.civil.services/us-states/backgrounds/1280x720/skyline/michigan.jpg","https://twitter.com/migov","https://www.facebook.com/MIgovernment" 24 | "Minnesota","minnesota","MN","North Star State / Land of 10,000 Lakes","https://mn.gov","1858-05-11",32,"Saint Paul","http://www.stpaul.gov",5420380,21,"http://www.house.leg.state.mn.us/cco/rules/mncon/preamble.htm","https://cdn.civil.services/us-states/flags/minnesota-large.png","https://cdn.civil.services/us-states/seals/minnesota-large.png","https://cdn.civil.services/us-states/maps/minnesota-large.png","https://cdn.civil.services/us-states/backgrounds/1280x720/landscape/minnesota.jpg","https://cdn.civil.services/us-states/backgrounds/1280x720/skyline/minnesota.jpg",, 25 | "Mississippi","mississippi","MS","Magnolia State","http://www.ms.gov","1817-12-10",20,"Jackson","http://www.city.jackson.ms.us",2991207,31,"http://law.justia.com/constitution/mississippi","https://cdn.civil.services/us-states/flags/mississippi-large.png","https://cdn.civil.services/us-states/seals/mississippi-large.png","https://cdn.civil.services/us-states/maps/mississippi-large.png","https://cdn.civil.services/us-states/backgrounds/1280x720/landscape/mississippi.jpg","https://cdn.civil.services/us-states/backgrounds/1280x720/skyline/mississippi.jpg","https://twitter.com/msdotgov","https://www.facebook.com/msdotgov" 26 | "Missouri","missouri","MO","Show Me State","https://www.mo.gov","1821-08-10",24,"Jefferson City","http://www.jeffcitymo.org",6044171,18,"http://www.moga.mo.gov/mostatutes/moconstn.html","https://cdn.civil.services/us-states/flags/missouri-large.png","https://cdn.civil.services/us-states/seals/missouri-large.png","https://cdn.civil.services/us-states/maps/missouri-large.png","https://cdn.civil.services/us-states/backgrounds/1280x720/landscape/missouri.jpg","https://cdn.civil.services/us-states/backgrounds/1280x720/skyline/missouri.jpg","https://twitter.com/MoGov","https://www.facebook.com/mogov" 27 | "Montana","montana","MT","Treasure State","http://mt.gov","1889-11-08",41,"Helena","http://www.ci.helena.mt.us",1015165,44,"http://courts.mt.gov/content/library/docs/72constit.pdf","https://cdn.civil.services/us-states/flags/montana-large.png","https://cdn.civil.services/us-states/seals/montana-large.png","https://cdn.civil.services/us-states/maps/montana-large.png","https://cdn.civil.services/us-states/backgrounds/1280x720/landscape/montana.jpg","https://cdn.civil.services/us-states/backgrounds/1280x720/skyline/montana.jpg",, 28 | "Nebraska","nebraska","NE","Cornhusker State","http://www.nebraska.gov","1867-03-01",37,"Lincoln","http://lincoln.ne.gov",1868516,37,"http://www.state.ne.us/legislative/statutes/C","https://cdn.civil.services/us-states/flags/nebraska-large.png","https://cdn.civil.services/us-states/seals/nebraska-large.png","https://cdn.civil.services/us-states/maps/nebraska-large.png","https://cdn.civil.services/us-states/backgrounds/1280x720/landscape/nebraska.jpg","https://cdn.civil.services/us-states/backgrounds/1280x720/skyline/nebraska.jpg","https://twitter.com/Nebraskagov","https://www.facebook.com/nebraska.gov" 29 | "Nevada","nevada","NV","The Silver State","http://nv.gov","1864-10-31",36,"Carson City","http://www.carson.org",2790136,35,"http://www.leg.state.nv.us/Const/NvConst.html","https://cdn.civil.services/us-states/flags/nevada-large.png","https://cdn.civil.services/us-states/seals/nevada-large.png","https://cdn.civil.services/us-states/maps/nevada-large.png","https://cdn.civil.services/us-states/backgrounds/1280x720/landscape/nevada.jpg","https://cdn.civil.services/us-states/backgrounds/1280x720/skyline/nevada.jpg",, 30 | "New Hampshire","new-hampshire","NH","Granite State","https://www.nh.gov","1788-06-21",9,"Concord","http://www.concordnh.gov",1323459,42,"http://www.state.nh.us/constitution/constitution.html","https://cdn.civil.services/us-states/flags/new-hampshire-large.png","https://cdn.civil.services/us-states/seals/new-hampshire-large.png","https://cdn.civil.services/us-states/maps/new-hampshire-large.png","https://cdn.civil.services/us-states/backgrounds/1280x720/landscape/new-hampshire.jpg","https://cdn.civil.services/us-states/backgrounds/1280x720/skyline/new-hampshire.jpg","https://twitter.com/nhgov", 31 | "New Jersey","new-jersey","NJ","Garden State","http://www.state.nj.us","1787-12-18",3,"Trenton","http://www.trentonnj.org",8899339,11,"http://www.njleg.state.nj.us/lawsconstitution/consearch.asp","https://cdn.civil.services/us-states/flags/new-jersey-large.png","https://cdn.civil.services/us-states/seals/new-jersey-large.png","https://cdn.civil.services/us-states/maps/new-jersey-large.png","https://cdn.civil.services/us-states/backgrounds/1280x720/landscape/new-jersey.jpg","https://cdn.civil.services/us-states/backgrounds/1280x720/skyline/new-jersey.jpg",, 32 | "New Mexico","new-mexico","NM","Land of Enchantment","http://www.newmexico.gov","1912-01-06",47,"Santa Fe","http://www.santafenm.gov",2085287,36,"http://www.loc.gov/law/guide/us-nm.html","https://cdn.civil.services/us-states/flags/new-mexico-large.png","https://cdn.civil.services/us-states/seals/new-mexico-large.png","https://cdn.civil.services/us-states/maps/new-mexico-large.png","https://cdn.civil.services/us-states/backgrounds/1280x720/landscape/new-mexico.jpg","https://cdn.civil.services/us-states/backgrounds/1280x720/skyline/new-mexico.jpg",, 33 | "New York","new-york","NY","Empire State","http://www.ny.gov","1788-07-26",11,"Albany","http://www.albanyny.org",19651127,3,"https://www.dos.ny.gov/info/constitution.htm","https://cdn.civil.services/us-states/flags/new-york-large.png","https://cdn.civil.services/us-states/seals/new-york-large.png","https://cdn.civil.services/us-states/maps/new-york-large.png","https://cdn.civil.services/us-states/backgrounds/1280x720/landscape/new-york.jpg","https://cdn.civil.services/us-states/backgrounds/1280x720/skyline/new-york.jpg","https://twitter.com/nygov", 34 | "North Carolina","north-carolina","NC","Old North State / Tar Heel State","http://www.nc.gov","1789-11-21",12,"Raleigh","http://www.raleigh-nc.org",9848060,10,"http://statelibrary.dcr.state.nc.us/nc/stgovt/preconst.htm","https://cdn.civil.services/us-states/flags/north-carolina-large.png","https://cdn.civil.services/us-states/seals/north-carolina-large.png","https://cdn.civil.services/us-states/maps/north-carolina-large.png","https://cdn.civil.services/us-states/backgrounds/1280x720/landscape/north-carolina.jpg","https://cdn.civil.services/us-states/backgrounds/1280x720/skyline/north-carolina.jpg","https://twitter.com/NCdotGov", 35 | "North Dakota","north-dakota","ND","Peace Garden State / Flickertail State / Roughrider State","http://www.nd.gov","1889-11-02",39,"Bismarck","http://www.bismarck.org",723393,48,"http://www.legis.nd.gov/information/statutes/const-laws.html","https://cdn.civil.services/us-states/flags/north-dakota-large.png","https://cdn.civil.services/us-states/seals/north-dakota-large.png","https://cdn.civil.services/us-states/maps/north-dakota-large.png","https://cdn.civil.services/us-states/backgrounds/1280x720/landscape/north-dakota.jpg","https://cdn.civil.services/us-states/backgrounds/1280x720/skyline/north-dakota.jpg","https://twitter.com/ExperienceND","https://www.facebook.com/ExperienceND" 36 | "Ohio","ohio","OH","Buckeye State","https://ohio.gov","1803-03-01",17,"Columbus","http://ci.columbus.oh.us",11570808,7,"http://www.legislature.state.oh.us/constitution.cfm","https://cdn.civil.services/us-states/flags/ohio-large.png","https://cdn.civil.services/us-states/seals/ohio-large.png","https://cdn.civil.services/us-states/maps/ohio-large.png","https://cdn.civil.services/us-states/backgrounds/1280x720/landscape/ohio.jpg","https://cdn.civil.services/us-states/backgrounds/1280x720/skyline/ohio.jpg","https://twitter.com/ohgov", 37 | "Oklahoma","oklahoma","OK","Sooner State","https://www.ok.gov","1907-11-16",46,"Oklahoma City","http://www.okc.gov",3850568,28,"http://oklegal.onenet.net/okcon","https://cdn.civil.services/us-states/flags/oklahoma-large.png","https://cdn.civil.services/us-states/seals/oklahoma-large.png","https://cdn.civil.services/us-states/maps/oklahoma-large.png","https://cdn.civil.services/us-states/backgrounds/1280x720/landscape/oklahoma.jpg","https://cdn.civil.services/us-states/backgrounds/1280x720/skyline/oklahoma.jpg","https://twitter.com/okgov","https://www.facebook.com/okgov" 38 | "Oregon","oregon","OR","Beaver State","http://www.oregon.gov","1859-02-14",33,"Salem","http://www.cityofsalem.net/Pages/default.aspx",3930065,27,"http://bluebook.state.or.us/state/constitution/constitution.htm","https://cdn.civil.services/us-states/flags/oregon-large.png","https://cdn.civil.services/us-states/seals/oregon-large.png","https://cdn.civil.services/us-states/maps/oregon-large.png","https://cdn.civil.services/us-states/backgrounds/1280x720/landscape/oregon.jpg","https://cdn.civil.services/us-states/backgrounds/1280x720/skyline/oregon.jpg",, 39 | "Pennsylvania","pennsylvania","PA","Keystone State","http://www.pa.gov","1787-12-12",2,"Harrisburg","http://harrisburgpa.gov",12773801,6,"http://sites.state.pa.us/PA_Constitution.html","https://cdn.civil.services/us-states/flags/pennsylvania-large.png","https://cdn.civil.services/us-states/seals/pennsylvania-large.png","https://cdn.civil.services/us-states/maps/pennsylvania-large.png","https://cdn.civil.services/us-states/backgrounds/1280x720/landscape/pennsylvania.jpg","https://cdn.civil.services/us-states/backgrounds/1280x720/skyline/pennsylvania.jpg","https://www.facebook.com/visitPA","https://twitter.com/visitPA" 40 | "Rhode Island","rhode-island","RI","The Ocean State","https://www.ri.gov","1790-05-29",13,"Providence","http://www.providenceri.com",1051511,43,"http://webserver.rilin.state.ri.us/RiConstitution","https://cdn.civil.services/us-states/flags/rhode-island-large.png","https://cdn.civil.services/us-states/seals/rhode-island-large.png","https://cdn.civil.services/us-states/maps/rhode-island-large.png","https://cdn.civil.services/us-states/backgrounds/1280x720/landscape/rhode-island.jpg","https://cdn.civil.services/us-states/backgrounds/1280x720/skyline/rhode-island.jpg","https://twitter.com/rigov","https://www.facebook.com/RIgov-Rhode-Island-Government-Online-24056655991" 41 | "South Carolina","south-carolina","SC","Palmetto State","http://www.sc.gov","1788-05-23",8,"Columbia","http://www.columbiasc.net",4774839,24,"http://www.scstatehouse.gov/scconstitution/scconst.php","https://cdn.civil.services/us-states/flags/south-carolina-large.png","https://cdn.civil.services/us-states/seals/south-carolina-large.png","https://cdn.civil.services/us-states/maps/south-carolina-large.png","https://cdn.civil.services/us-states/backgrounds/1280x720/landscape/south-carolina.jpg","https://cdn.civil.services/us-states/backgrounds/1280x720/skyline/south-carolina.jpg","https://twitter.com/scgov","http://www.facebook.com/pages/SCgov/12752057990" 42 | "South Dakota","south-dakota","SD","Mount Rushmore State","http://sd.gov","1889-11-02",40,"Pierre","http://ci.pierre.sd.us",844877,46,"http://legis.sd.gov/statutes/Constitution","https://cdn.civil.services/us-states/flags/south-dakota-large.png","https://cdn.civil.services/us-states/seals/south-dakota-large.png","https://cdn.civil.services/us-states/maps/south-dakota-large.png","https://cdn.civil.services/us-states/backgrounds/1280x720/landscape/south-dakota.jpg","https://cdn.civil.services/us-states/backgrounds/1280x720/skyline/south-dakota.jpg",, 43 | "Tennessee","tennessee","TN","Volunteer State","https://www.tn.gov","1796-06-01",16,"Nashville","http://www.nashville.gov",6495978,17,"http://www.capitol.tn.gov/about/docs/TN-Constitution.pdf","https://cdn.civil.services/us-states/flags/tennessee-large.png","https://cdn.civil.services/us-states/seals/tennessee-large.png","https://cdn.civil.services/us-states/maps/tennessee-large.png","https://cdn.civil.services/us-states/backgrounds/1280x720/landscape/tennessee.jpg","https://cdn.civil.services/us-states/backgrounds/1280x720/skyline/tennessee.jpg","https://twitter.com/TNVacation","https://www.facebook.com/tnvacation" 44 | "Texas","texas","TX","Lone Star State","https://www.texas.gov","1845-12-29",28,"Austin","http://www.austintexas.gov",26448193,2,"http://www.constitution.legis.state.tx.us","https://cdn.civil.services/us-states/flags/texas-large.png","https://cdn.civil.services/us-states/seals/texas-large.png","https://cdn.civil.services/us-states/maps/texas-large.png","https://cdn.civil.services/us-states/backgrounds/1280x720/landscape/texas.jpg","https://cdn.civil.services/us-states/backgrounds/1280x720/skyline/texas.jpg","https://twitter.com/texasgov","http://www.facebook.com/Texas.gov" 45 | "Utah","utah","UT","The Beehive State","https://utah.gov","1896-01-04",45,"Salt Lake City","http://www.slcgov.com",2900872,33,"http://le.utah.gov/UtahCode/chapter.jsp?code=Constitution","https://cdn.civil.services/us-states/flags/utah-large.png","https://cdn.civil.services/us-states/seals/utah-large.png","https://cdn.civil.services/us-states/maps/utah-large.png","https://cdn.civil.services/us-states/backgrounds/1280x720/landscape/utah.jpg","https://cdn.civil.services/us-states/backgrounds/1280x720/skyline/utah.jpg","https://twitter.com/UtahGov","https://www.facebook.com/utahgov" 46 | "Vermont","vermont","VT","Green Mountain State","http://vermont.gov","1791-03-04",14,"Montpelier","http://www.montpelier-vt.org",626630,49,"http://www.leg.state.vt.us/statutes/const2.htm","https://cdn.civil.services/us-states/flags/vermont-large.png","https://cdn.civil.services/us-states/seals/vermont-large.png","https://cdn.civil.services/us-states/maps/vermont-large.png","https://cdn.civil.services/us-states/backgrounds/1280x720/landscape/vermont.jpg","https://cdn.civil.services/us-states/backgrounds/1280x720/skyline/vermont.jpg","https://twitter.com/vermontgov","https://www.facebook.com/MyVermont" 47 | "Virginia","virginia","VA","Old Dominion State","https://www.virginia.gov","1788-06-25",10,"Richmond","http://www.richmondgov.com",8260405,12,"http://hodcap.state.va.us/publications/Constitution-01-13.pdf","https://cdn.civil.services/us-states/flags/virginia-large.png","https://cdn.civil.services/us-states/seals/virginia-large.png","https://cdn.civil.services/us-states/maps/virginia-large.png","https://cdn.civil.services/us-states/backgrounds/1280x720/landscape/virginia.jpg","https://cdn.civil.services/us-states/backgrounds/1280x720/skyline/virginia.jpg",, 48 | "Washington","washington","WA","The Evergreen State","http://www.wa.gov","1889-11-11",42,"Olympia","http://www.ci.olympia.wa.us",6971406,13,"http://www.leg.wa.gov/lawsandagencyrules/pages/constitution.aspx","https://cdn.civil.services/us-states/flags/washington-large.png","https://cdn.civil.services/us-states/seals/washington-large.png","https://cdn.civil.services/us-states/maps/washington-large.png","https://cdn.civil.services/us-states/backgrounds/1280x720/landscape/washington.jpg","https://cdn.civil.services/us-states/backgrounds/1280x720/skyline/washington.jpg","https://twitter.com/wagov","" 49 | "West Virginia","west-virginia","WV","Mountain State","http://www.wv.gov","1863-06-20",35,"Charleston","http://www.cityofcharleston.org",1854304,38,"http://www.legis.state.wv.us/WVCODE/WV_CON.cfm","https://cdn.civil.services/us-states/flags/west-virginia-large.png","https://cdn.civil.services/us-states/seals/west-virginia-large.png","https://cdn.civil.services/us-states/maps/west-virginia-large.png","https://cdn.civil.services/us-states/backgrounds/1280x720/landscape/west-virginia.jpg","https://cdn.civil.services/us-states/backgrounds/1280x720/skyline/west-virginia.jpg","https://twitter.com/wvgov","https://www.facebook.com/wvgov" 50 | "Wisconsin","wisconsin","WI","Badger State","https://www.wisconsin.gov","1848-05-29",30,"Madison","http://www.ci.madison.wi.us",5742713,20,"http://www.legis.state.wi.us/rsb/2wiscon.html","https://cdn.civil.services/us-states/flags/wisconsin-large.png","https://cdn.civil.services/us-states/seals/wisconsin-large.png","https://cdn.civil.services/us-states/maps/wisconsin-large.png","https://cdn.civil.services/us-states/backgrounds/1280x720/landscape/wisconsin.jpg","https://cdn.civil.services/us-states/backgrounds/1280x720/skyline/wisconsin.jpg",, 51 | "Wyoming","wyoming","WY","Equality State","http://www.wyo.gov","1890-07-10",44,"Cheyenne","http://www.cheyennecity.org",582658,50,"http://legisweb.state.wy.us/statutes/constitution.aspx?file=titles/97Title97.htm","https://cdn.civil.services/us-states/flags/wyoming-large.png","https://cdn.civil.services/us-states/seals/wyoming-large.png","https://cdn.civil.services/us-states/maps/wyoming-large.png","https://cdn.civil.services/us-states/backgrounds/1280x720/landscape/wyoming.jpg","https://cdn.civil.services/us-states/backgrounds/1280x720/skyline/wyoming.jpg",, -------------------------------------------------------------------------------- /backend/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3.1' 2 | services: 3 | db: 4 | image: postgres 5 | restart: always 6 | volumes: 7 | - ./docker-data/db-data:/var/lib/postgresql/data 8 | environment: 9 | POSTGRES_PASSWORD: ${POSTGRES_PASSWORD} 10 | POSTGRES_USER: ${POSTGRES_USER} 11 | POSTGRES_DB: ${POSTGRES_DB} 12 | ports: 13 | - 5432:5432 14 | adminer: 15 | depends_on: 16 | - db 17 | image: adminer 18 | restart: always 19 | ports: 20 | - 8090:8080 -------------------------------------------------------------------------------- /backend/jest.config.js: -------------------------------------------------------------------------------- 1 | // For a detailed explanation regarding each configuration property, visit: 2 | // https://jestjs.io/docs/en/configuration.html 3 | 4 | module.exports = { 5 | // All imported modules in your tests should be mocked automatically 6 | // automock: false, 7 | 8 | // Stop running tests after `n` failures 9 | // bail: 0, 10 | 11 | // Respect "browser" field in package.json when resolving modules 12 | // browser: false, 13 | 14 | // The directory where Jest should store its cached dependency information 15 | // cacheDirectory: "/private/var/folders/38/js1ss8ms4j7dv88k55xt9bg80000gn/T/jest_dx", 16 | 17 | // Automatically clear mock calls and instances between every test 18 | // clearMocks: false, 19 | 20 | // Indicates whether the coverage information should be collected while executing the test 21 | // collectCoverage: false, 22 | 23 | // An array of glob patterns indicating a set of files for which coverage information should be collected 24 | // collectCoverageFrom: undefined, 25 | 26 | // The directory where Jest should output its coverage files 27 | coverageDirectory: 'coverage', 28 | 29 | // An array of regexp pattern strings used to skip coverage collection 30 | // coveragePathIgnorePatterns: [ 31 | // "/node_modules/" 32 | // ], 33 | 34 | // A list of reporter names that Jest uses when writing coverage reports 35 | // coverageReporters: [ 36 | // "json", 37 | // "text", 38 | // "lcov", 39 | // "clover" 40 | // ], 41 | 42 | // An object that configures minimum threshold enforcement for coverage results 43 | // coverageThreshold: undefined, 44 | 45 | // A path to a custom dependency extractor 46 | // dependencyExtractor: undefined, 47 | 48 | // Make calling deprecated APIs throw helpful error messages 49 | // errorOnDeprecated: false, 50 | 51 | // Force coverage collection from ignored files using an array of glob patterns 52 | // forceCoverageMatch: [], 53 | 54 | // A path to a module which exports an async function that is triggered once before all test suites 55 | globalSetup: './src/setupTests.js', 56 | 57 | // A path to a module which exports an async function that is triggered once after all test suites 58 | globalTeardown: './src/teardownTests.js', 59 | 60 | // A set of global variables that need to be available in all test environments 61 | // globals: {}, 62 | 63 | // The maximum amount of workers used to run your tests. Can be specified as % or a number. E.g. maxWorkers: 10% will use 10% of your CPU amount + 1 as the maximum worker number. maxWorkers: 2 will use a maximum of 2 workers. 64 | // maxWorkers: "50%", 65 | 66 | // An array of directory names to be searched recursively up from the requiring module's location 67 | // moduleDirectories: [ 68 | // "node_modules" 69 | // ], 70 | 71 | // An array of file extensions your modules use 72 | // moduleFileExtensions: [ 73 | // "js", 74 | // "json", 75 | // "jsx", 76 | // "ts", 77 | // "tsx", 78 | // "node" 79 | // ], 80 | 81 | // A map from regular expressions to module names or to arrays of module names that allow to stub out resources with a single module 82 | // moduleNameMapper: {}, 83 | 84 | // An array of regexp pattern strings, matched against all module paths before considered 'visible' to the module loader 85 | // modulePathIgnorePatterns: [], 86 | 87 | // Activates notifications for test results 88 | // notify: false, 89 | 90 | // An enum that specifies notification mode. Requires { notify: true } 91 | // notifyMode: "failure-change", 92 | 93 | // A preset that is used as a base for Jest's configuration 94 | // preset: undefined, 95 | 96 | // Run tests from one or more projects 97 | // projects: undefined, 98 | 99 | // Use this configuration option to add custom reporters to Jest 100 | // reporters: undefined, 101 | 102 | // Automatically reset mock state between every test 103 | // resetMocks: false, 104 | 105 | // Reset the module registry before running each individual test 106 | // resetModules: false, 107 | 108 | // A path to a custom resolver 109 | // resolver: undefined, 110 | 111 | // Automatically restore mock state between every test 112 | // restoreMocks: false, 113 | 114 | // The root directory that Jest should scan for tests and modules within 115 | // rootDir: undefined, 116 | 117 | // A list of paths to directories that Jest should use to search for files in 118 | // roots: [ 119 | // "" 120 | // ], 121 | 122 | // Allows you to use a custom runner instead of Jest's default test runner 123 | // runner: "jest-runner", 124 | 125 | // The paths to modules that run some code to configure or set up the testing environment before each test 126 | // setupFiles: [], 127 | 128 | // A list of paths to modules that run some code to configure or set up the testing framework before each test 129 | setupFilesAfterEnv: ['./src/setupFilesAfterEnv.js'], 130 | 131 | // A list of paths to snapshot serializer modules Jest should use for snapshot testing 132 | // snapshotSerializers: [], 133 | 134 | // The test environment that will be used for testing 135 | testEnvironment: 'node', 136 | 137 | // Options that will be passed to the testEnvironment 138 | // testEnvironmentOptions: {}, 139 | 140 | // Adds a location field to test results 141 | // testLocationInResults: false, 142 | 143 | // The glob patterns Jest uses to detect test files 144 | // testMatch: [ 145 | // "**/__tests__/**/*.[jt]s?(x)", 146 | // "**/?(*.)+(spec|test).[tj]s?(x)" 147 | // ], 148 | 149 | // An array of regexp pattern strings that are matched against all test paths, matched tests are skipped 150 | // testPathIgnorePatterns: [ 151 | // "/node_modules/" 152 | // ], 153 | 154 | // The regexp pattern or array of patterns that Jest uses to detect test files 155 | // testRegex: [], 156 | 157 | // This option allows the use of a custom results processor 158 | // testResultsProcessor: undefined, 159 | 160 | // This option allows use of a custom test runner 161 | // testRunner: "jasmine2", 162 | 163 | // This option sets the URL for the jsdom environment. It is reflected in properties such as location.href 164 | // testURL: "http://localhost", 165 | 166 | // Setting this value to "fake" allows the use of fake timers for functions such as "setTimeout" 167 | // timers: "real", 168 | 169 | // A map from regular expressions to paths to transformers 170 | // transform: undefined, 171 | 172 | // An array of regexp pattern strings that are matched against all source file paths, matched files will skip transformation 173 | // transformIgnorePatterns: [ 174 | // "/node_modules/" 175 | // ], 176 | 177 | // An array of regexp pattern strings that are matched against all modules before the module loader will automatically return a mock for them 178 | // unmockedModulePathPatterns: undefined, 179 | 180 | // Indicates whether each individual test should be reported during the run 181 | // verbose: undefined, 182 | 183 | // An array of regexp patterns that are matched against all source file paths before re-running tests in watch mode 184 | // watchPathIgnorePatterns: [], 185 | 186 | // Whether to use watchman for file crawling 187 | // watchman: true, 188 | }; 189 | -------------------------------------------------------------------------------- /backend/knexfile.js: -------------------------------------------------------------------------------- 1 | require('dotenv').config(); 2 | 3 | module.exports = { 4 | development: { 5 | client: 'pg', 6 | connection: { 7 | database: process.env.POSTGRES_DB, 8 | user: process.env.POSTGRES_USER, 9 | password: process.env.POSTGRES_PASSWORD, 10 | }, 11 | migrations: { 12 | directory: './db/migrations', 13 | }, 14 | seeds: { 15 | directory: './db/seeds', 16 | }, 17 | }, 18 | test: { 19 | client: 'pg', 20 | connection: { 21 | // TODO: update postgres container to create test db on start 22 | database: process.env.POSTGRES_TEST_DB, 23 | user: process.env.POSTGRES_USER, 24 | password: process.env.POSTGRES_PASSWORD, 25 | }, 26 | migrations: { 27 | directory: './db/migrations', 28 | }, 29 | seeds: { 30 | directory: './db/seeds', 31 | }, 32 | }, 33 | }; 34 | -------------------------------------------------------------------------------- /backend/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "backend", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "src/index.js", 6 | "scripts": { 7 | "start": "node src/index.js", 8 | "dev": "DEBUG=inventory-app nodemon src/index.js", 9 | "migrate": "DEBUG=inventory-app knex migrate:latest", 10 | "rollback": "DEBUG=inventory-app knex migrate:rollback", 11 | "seed": "DEBUG=inventory-app knex seed:run", 12 | "test": "DEBUG=inventory-app jest", 13 | "lint": "eslint db src" 14 | }, 15 | "keywords": [], 16 | "author": "CJ R. (https://w3cj.now.sh)", 17 | "license": "MIT", 18 | "dependencies": { 19 | "bcrypt": "^5.0.1", 20 | "compression": "^1.7.4", 21 | "dotenv": "^16.0.1", 22 | "express": "^4.18.1", 23 | "helmet": "^5.1.1", 24 | "jsonwebtoken": "^8.5.1", 25 | "knex": "^2.2.0", 26 | "morgan": "^1.10.0", 27 | "objection": "^3.0.1", 28 | "papaparse": "^5.3.2", 29 | "pg": "^8.7.3", 30 | "yup": "^0.32.11" 31 | }, 32 | "devDependencies": { 33 | "eslint": "^8.21.0", 34 | "eslint-config-airbnb-base": "^15.0.0", 35 | "eslint-config-prettier": "^8.5.0", 36 | "eslint-plugin-import": "^2.26.0", 37 | "eslint-plugin-prettier": "^4.2.1", 38 | "jest": "^28.1.3", 39 | "nodemon": "^2.0.19", 40 | "prettier": "^2.7.1", 41 | "supertest": "^6.2.4" 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /backend/src/api/addresses/address.schema.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json-schema.org/draft-07/schema", 3 | "type": "object", 4 | "title": "Address", 5 | "description": "The Address", 6 | "required": [ 7 | "street_address_1", 8 | "city", 9 | "zipcode", 10 | "country_id", 11 | "latitude", 12 | "longitude" 13 | ], 14 | "additionalProperties": false, 15 | "properties": { 16 | "id": { 17 | "$id": "#/properties/id", 18 | "type": "integer", 19 | "title": "The database ID of the address." 20 | }, 21 | "street_address_1": { 22 | "$id": "#/properties/street_address_1", 23 | "type": "string", 24 | "title": "The street address." 25 | }, 26 | "street_address_2": { 27 | "$id": "#/properties/street_address_2", 28 | "type": "string", 29 | "title": "The street address 2." 30 | }, 31 | "city": { 32 | "$id": "#/properties/city", 33 | "type": "string", 34 | "title": "The city name." 35 | }, 36 | "zipcode": { 37 | "$id": "#/properties/zipcode", 38 | "type": "string", 39 | "title": "The zipcode." 40 | }, 41 | "latitude": { 42 | "$id": "#/properties/latitude", 43 | "type": "number", 44 | "title": "The latitude." 45 | }, 46 | "longitude": { 47 | "$id": "#/properties/longitude", 48 | "type": "number", 49 | "title": "The longitude." 50 | }, 51 | "state_id": { 52 | "$id": "#/properties/state_id", 53 | "type": "number", 54 | "title": "The id of the state." 55 | }, 56 | "country_id": { 57 | "$id": "#/properties/country_id", 58 | "type": "number", 59 | "title": "The id of the country." 60 | } 61 | } 62 | } -------------------------------------------------------------------------------- /backend/src/api/addresses/addresses.model.js: -------------------------------------------------------------------------------- 1 | const { Model } = require('objection'); 2 | 3 | const tableNames = require('../../constants/tableNames'); 4 | const schema = require('./address.schema.json'); 5 | 6 | class Address extends Model { 7 | static get tableName() { 8 | return tableNames.address; 9 | } 10 | 11 | static get jsonSchema() { 12 | return schema; 13 | } 14 | } 15 | 16 | module.exports = Address; 17 | -------------------------------------------------------------------------------- /backend/src/api/addresses/addresses.routes.js: -------------------------------------------------------------------------------- 1 | const express = require('express'); 2 | 3 | const Address = require('./addresses.model'); 4 | 5 | const router = express.Router(); 6 | 7 | router.get('/', async (req, res, next) => { 8 | try { 9 | const addresses = await Address.query().where('deleted_at', null); 10 | res.json(addresses); 11 | } catch (error) { 12 | next(error); 13 | } 14 | }); 15 | 16 | router.post('/', async (req, res, next) => { 17 | try { 18 | ['street_address_1', 'street_address_2', 'city', 'zipcode'].forEach( 19 | (prop) => { 20 | if (req.body[prop]) { 21 | req.body[prop] = req.body[prop].toString().toLowerCase().trim(); 22 | } 23 | } 24 | ); 25 | const address = await Address.query().insert(req.body); 26 | res.json(address); 27 | } catch (error) { 28 | next(error); 29 | } 30 | }); 31 | 32 | module.exports = router; 33 | -------------------------------------------------------------------------------- /backend/src/api/addresses/addresses.test.js: -------------------------------------------------------------------------------- 1 | const supertest = require('supertest'); 2 | 3 | const app = require('../../app'); 4 | 5 | describe('GET /api/v1/addresses', () => { 6 | it('should respond with an array of addresses', async () => { 7 | const response = await supertest(app) 8 | .get('/api/v1/addresses') 9 | .expect('Content-Type', /json/) 10 | .expect(200); 11 | 12 | expect(response.body).toBeInstanceOf(Array); 13 | }); 14 | }); 15 | -------------------------------------------------------------------------------- /backend/src/api/api.test.js: -------------------------------------------------------------------------------- 1 | const supertest = require('supertest'); 2 | 3 | const app = require('../app'); 4 | const project = require('../constants/project'); 5 | 6 | describe('GET /api/v1', () => { 7 | it('should respond with a message', async () => { 8 | const response = await supertest(app) 9 | .get('/api/v1') 10 | .expect('Content-Type', /json/) 11 | .expect(200); 12 | 13 | expect(response.body.message).toEqual(project.message); 14 | }); 15 | }); 16 | -------------------------------------------------------------------------------- /backend/src/api/auth/auth.middlewares.js: -------------------------------------------------------------------------------- 1 | const jwt = require('../../lib/jwt'); 2 | const logger = require('../../lib/logger'); 3 | 4 | const setUserFromTokenIfValid = async (req, res, next) => { 5 | const authHeader = req.get('authorization'); 6 | if (authHeader) { 7 | try { 8 | const [, token] = authHeader.split(' '); 9 | const user = await jwt.verify(token); 10 | req.user = user; 11 | // TODO: maybe verify user against DB? 12 | // TODO: this is no longer stateless... 13 | return next(); 14 | } catch (error) { 15 | logger.error(error); 16 | } 17 | } 18 | return next(); 19 | }; 20 | 21 | const isLoggedIn = (req, res, next) => { 22 | if (req.user) { 23 | return next(); 24 | } 25 | const error = new Error('Un-Authorized'); 26 | res.status(401); 27 | return next(error); 28 | }; 29 | 30 | module.exports = { 31 | setUserFromTokenIfValid, 32 | isLoggedIn, 33 | }; 34 | -------------------------------------------------------------------------------- /backend/src/api/auth/auth.routes.js: -------------------------------------------------------------------------------- 1 | const express = require('express'); 2 | const yup = require('yup'); 3 | // TODO: extract to general hashing util 4 | const bcrypt = require('bcrypt'); 5 | 6 | const jwt = require('../../lib/jwt'); 7 | const User = require('../users/users.model'); 8 | 9 | const router = express.Router(); 10 | 11 | const schema = yup.object().shape({ 12 | name: yup.string().trim().min(2).required(), 13 | email: yup.string().trim().email().required(), 14 | password: yup 15 | .string() 16 | .min(8) 17 | .max(200) 18 | .matches(/[^A-Za-z0-9]/, 'password must contain a special character') 19 | .matches(/[A-Z]/, 'password must contain an uppercase letter') 20 | .matches(/[a-z]/, 'password must contain a lowercase letter') 21 | .matches(/[0-9]/, 'password must contain a number') 22 | .required(), 23 | }); 24 | 25 | const errorMessages = { 26 | invalidLogin: 'Invalid login.', 27 | emailInUse: 'Email in use.', 28 | }; 29 | 30 | router.post('/signup', async (req, res, next) => { 31 | const { name, email, password } = req.body; 32 | try { 33 | const createUser = { 34 | name, 35 | email, 36 | password, 37 | }; 38 | await schema.validate(createUser, { 39 | abortEarly: false, 40 | }); 41 | const existingUser = await User.query().where({ email }).first(); 42 | if (existingUser) { 43 | const error = new Error(errorMessages.emailInUse); 44 | res.status(403); 45 | throw error; 46 | } 47 | // TODO: get rounds from config 48 | const hashedPassword = await bcrypt.hash(password, 12); 49 | const insertedUser = await User.query().insert({ 50 | name, 51 | email, 52 | password: hashedPassword, 53 | }); 54 | delete insertedUser.password; 55 | const payload = { 56 | id: insertedUser.id, 57 | name, 58 | email, 59 | }; 60 | const token = await jwt.sign(payload); 61 | res.json({ 62 | user: payload, 63 | token, 64 | }); 65 | } catch (error) { 66 | next(error); 67 | } 68 | }); 69 | 70 | // TODO: stateless refresh tokens... 71 | router.post('/signin', async (req, res, next) => { 72 | const { email, password } = req.body; 73 | try { 74 | // TODO: should we validate password with regex on sigin? 75 | // TODO: password validation changes would effect old users... 76 | await schema.validate( 77 | { 78 | name: 'DocD', 79 | email, 80 | password, 81 | }, 82 | { 83 | abortEarly: false, 84 | } 85 | ); 86 | const user = await User.query().where({ email }).first(); 87 | if (!user) { 88 | const error = new Error(errorMessages.invalidLogin); 89 | res.status(403); 90 | throw error; 91 | } 92 | const validPassword = await bcrypt.compare(password, user.password); 93 | if (!validPassword) { 94 | const error = new Error(errorMessages.invalidLogin); 95 | res.status(403); 96 | throw error; 97 | } 98 | const payload = { 99 | id: user.id, 100 | name: user.name, 101 | // TODO: get roles from DB 102 | roles: ['admin', 'list:users'], 103 | }; 104 | const token = await jwt.sign(payload); 105 | res.json({ 106 | user: payload, 107 | token, 108 | }); 109 | } catch (error) { 110 | next(error); 111 | } 112 | }); 113 | 114 | module.exports = router; 115 | -------------------------------------------------------------------------------- /backend/src/api/companies/companies.model.js: -------------------------------------------------------------------------------- 1 | const { Model } = require('objection'); 2 | 3 | const tableNames = require('../../constants/tableNames'); 4 | const schema = require('./companies.schema.json'); 5 | 6 | class Company extends Model { 7 | static get tableName() { 8 | return tableNames.company; 9 | } 10 | 11 | static get jsonSchema() { 12 | return schema; 13 | } 14 | } 15 | 16 | module.exports = Company; 17 | -------------------------------------------------------------------------------- /backend/src/api/companies/companies.routes.js: -------------------------------------------------------------------------------- 1 | const express = require('express'); 2 | 3 | const Company = require('./companies.model'); 4 | 5 | const router = express.Router(); 6 | 7 | router.get('/', async (req, res, next) => { 8 | try { 9 | const companies = await Company.query().where('deleted_at', null); 10 | res.json(companies); 11 | } catch (error) { 12 | next(error); 13 | } 14 | }); 15 | 16 | router.post('/', async (req, res, next) => { 17 | try { 18 | const company = await Company.query().insert(req.body); 19 | res.json(company); 20 | } catch (error) { 21 | next(error); 22 | } 23 | }); 24 | 25 | module.exports = router; 26 | -------------------------------------------------------------------------------- /backend/src/api/companies/companies.schema.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json-schema.org/draft-07/schema", 3 | "type": "object", 4 | "title": "Company", 5 | "description": "The Company", 6 | "required": [ 7 | "name", 8 | "address_id" 9 | ], 10 | "additionalProperties": false, 11 | "properties": { 12 | "id": { 13 | "$id": "#/properties/id", 14 | "type": "integer", 15 | "title": "The database ID of the company." 16 | }, 17 | "name": { 18 | "$id": "#/properties/name", 19 | "type": "string", 20 | "title": "The name of the company." 21 | }, 22 | "logo_url": { 23 | "$id": "#/properties/logo_url", 24 | "type": "string", 25 | "title": "The company logo url." 26 | }, 27 | "description": { 28 | "$id": "#/properties/description", 29 | "type": "string", 30 | "title": "The company description" 31 | }, 32 | "website_url": { 33 | "$id": "#/properties/website_url", 34 | "type": "string", 35 | "title": "The website url." 36 | }, 37 | "email": { 38 | "$id": "#/properties/email", 39 | "type": "string", 40 | "title": "The company email." 41 | }, 42 | "address_id": { 43 | "$id": "#/properties/address_id", 44 | "type": "number", 45 | "title": "The id of the address." 46 | } 47 | } 48 | } -------------------------------------------------------------------------------- /backend/src/api/companies/companies.test.js: -------------------------------------------------------------------------------- 1 | const supertest = require('supertest'); 2 | 3 | const app = require('../../app'); 4 | 5 | describe('GET /api/v1/companies', () => { 6 | it('should respond with an array of companies', async () => { 7 | const response = await supertest(app) 8 | .get('/api/v1/companies') 9 | .expect('Content-Type', /json/) 10 | .expect(200); 11 | 12 | expect(response.body).toBeInstanceOf(Array); 13 | }); 14 | }); 15 | -------------------------------------------------------------------------------- /backend/src/api/index.js: -------------------------------------------------------------------------------- 1 | const express = require('express'); 2 | 3 | const project = require('../constants/project'); 4 | const authMiddlewares = require('./auth/auth.middlewares'); 5 | 6 | const states = require('./states/states.routes'); 7 | const users = require('./users/users.routes'); 8 | const addresses = require('./addresses/addresses.routes'); 9 | const companies = require('./companies/companies.routes'); 10 | const items = require('./items/items.routes'); 11 | const auth = require('./auth/auth.routes'); 12 | 13 | const router = express.Router(); 14 | 15 | router.get('/', (req, res) => { 16 | res.json({ 17 | message: project.message, 18 | }); 19 | }); 20 | 21 | router.use('/states', authMiddlewares.isLoggedIn, states); 22 | router.use('/users', authMiddlewares.isLoggedIn, users); 23 | router.use('/auth', auth); 24 | router.use('/addresses', authMiddlewares.isLoggedIn, addresses); 25 | router.use('/companies', authMiddlewares.isLoggedIn, companies); 26 | router.use('/items', authMiddlewares.isLoggedIn, items); 27 | 28 | module.exports = router; 29 | -------------------------------------------------------------------------------- /backend/src/api/items/item_infos/item_infos.model.js: -------------------------------------------------------------------------------- 1 | const { Model } = require('objection'); 2 | 3 | const tableNames = require('../../../constants/tableNames'); 4 | const schema = require('./item_infos.schema.json'); 5 | 6 | class ItemInfos extends Model { 7 | static get tableName() { 8 | return tableNames.item_info; 9 | } 10 | 11 | static get jsonSchema() { 12 | return schema; 13 | } 14 | } 15 | 16 | module.exports = ItemInfos; 17 | -------------------------------------------------------------------------------- /backend/src/api/items/item_infos/item_infos.routes.js: -------------------------------------------------------------------------------- 1 | const express = require('express'); 2 | 3 | const ItemInfo = require('./item_infos.model'); 4 | 5 | const router = express.Router({ mergeParams: true }); 6 | 7 | router.get('/', async (req, res, next) => { 8 | try { 9 | const items = await ItemInfo.query().where('deleted_at', null); 10 | res.json(items); 11 | } catch (error) { 12 | next(error); 13 | } 14 | }); 15 | 16 | router.post('/', async (req, res, next) => { 17 | try { 18 | // TODO: set user id by logged in user 19 | req.body.item_id = Number(req.params.item_id); 20 | const item = await ItemInfo.query().insert(req.body); 21 | res.json(item); 22 | } catch (error) { 23 | next(error); 24 | } 25 | }); 26 | 27 | router.patch('/:id', async (req, res, next) => { 28 | try { 29 | const item = await ItemInfo.query().patchAndFetchById( 30 | req.params.id, 31 | req.body 32 | ); 33 | res.json(item); 34 | } catch (error) { 35 | next(error); 36 | } 37 | }); 38 | 39 | module.exports = router; 40 | -------------------------------------------------------------------------------- /backend/src/api/items/item_infos/item_infos.schema.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json-schema.org/draft-07/schema", 3 | "type": "object", 4 | "title": "Item Info", 5 | "description": "The Item Info", 6 | "required": [ 7 | "user_id", 8 | "item_id", 9 | "purchase_date", 10 | "inventory_location_id" 11 | ], 12 | "additionalProperties": false, 13 | "properties": { 14 | "id": { 15 | "$id": "#/properties/id", 16 | "type": "integer", 17 | "title": "The database ID of the Item." 18 | }, 19 | "purchase_date": { 20 | "$id": "#/properties/purchase_date", 21 | "type": "string", 22 | "format": "date", 23 | "title": "The date of purchase" 24 | }, 25 | "expiration_date": { 26 | "$id": "#/properties/expiration_date", 27 | "type": "string", 28 | "format": "date", 29 | "title": "The date of expiration" 30 | }, 31 | "last_used": { 32 | "$id": "#/properties/last_used", 33 | "type": "string", 34 | "format": "date", 35 | "title": "The date of last use." 36 | }, 37 | "retailer_id": { 38 | "$id": "#/properties/retailer_id", 39 | "type": "integer", 40 | "title": "The id of the retailer." 41 | }, 42 | "purchase_price": { 43 | "$id": "#/properties/purchase_price", 44 | "minimum": 0, 45 | "type": "number", 46 | "title": "The purchase price." 47 | }, 48 | "msrp": { 49 | "$id": "#/properties/msrp", 50 | "minimum": 0, 51 | "type": "number", 52 | "title": "The msrp." 53 | }, 54 | "item_id": { 55 | "$id": "#/properties/item_id", 56 | "type": "integer", 57 | "title": "The id of the parent item." 58 | }, 59 | "user_id": { 60 | "$id": "#/properties/user_id", 61 | "type": "number", 62 | "title": "The id of the user." 63 | }, 64 | "inventory_location_id": { 65 | "$id": "#/properties/inventory_location_id", 66 | "type": "number", 67 | "title": "The id of the inventory location." 68 | } 69 | } 70 | } -------------------------------------------------------------------------------- /backend/src/api/items/items.model.js: -------------------------------------------------------------------------------- 1 | const { Model } = require('objection'); 2 | 3 | const tableNames = require('../../constants/tableNames'); 4 | const schema = require('./items.schema.json'); 5 | const ItemInfo = require('./item_infos/item_infos.model'); 6 | 7 | class Item extends Model { 8 | static get tableName() { 9 | return tableNames.item; 10 | } 11 | 12 | static get jsonSchema() { 13 | return schema; 14 | } 15 | 16 | static get relationMappings() { 17 | return { 18 | item_infos: { 19 | relation: Model.HasManyRelation, 20 | modelClass: ItemInfo, 21 | join: { 22 | from: `${tableNames.item}.id`, 23 | to: `${tableNames.item_info}.item_id`, 24 | }, 25 | }, 26 | }; 27 | } 28 | } 29 | 30 | module.exports = Item; 31 | -------------------------------------------------------------------------------- /backend/src/api/items/items.routes.js: -------------------------------------------------------------------------------- 1 | const express = require('express'); 2 | 3 | const Item = require('./items.model'); 4 | const itemInfos = require('./item_infos/item_infos.routes'); 5 | 6 | const router = express.Router({ 7 | mergeParams: true, 8 | }); 9 | 10 | router.use('/:item_id/item_infos', itemInfos); 11 | 12 | router.get('/', async (req, res, next) => { 13 | try { 14 | const items = await Item.query().where('deleted_at', null); 15 | res.json(items); 16 | } catch (error) { 17 | next(error); 18 | } 19 | }); 20 | 21 | router.get('/:id', async (req, res, next) => { 22 | try { 23 | const items = await Item.query() 24 | .where('deleted_at', null) 25 | .andWhere('id', req.params.id) 26 | // .withGraphJoined('item_infos') // TODO: make this work 27 | .withGraphFetched('item_infos') 28 | .first(); 29 | res.json(items); 30 | } catch (error) { 31 | next(error); 32 | } 33 | }); 34 | 35 | router.post('/', async (req, res, next) => { 36 | try { 37 | // TODO: set user id by logged in user 38 | const item = await Item.query().insert(req.body); 39 | res.json(item); 40 | } catch (error) { 41 | next(error); 42 | } 43 | }); 44 | 45 | module.exports = router; 46 | -------------------------------------------------------------------------------- /backend/src/api/items/items.schema.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json-schema.org/draft-07/schema", 3 | "type": "object", 4 | "title": "Item", 5 | "description": "The Item", 6 | "required": [ 7 | "name", 8 | "item_type_id", 9 | "company_id" 10 | ], 11 | "additionalProperties": false, 12 | "properties": { 13 | "id": { 14 | "$id": "#/properties/id", 15 | "type": "integer", 16 | "title": "The database ID of the Item." 17 | }, 18 | "name": { 19 | "$id": "#/properties/name", 20 | "type": "string", 21 | "title": "The name of the Item." 22 | }, 23 | "description": { 24 | "$id": "#/properties/description", 25 | "type": "string", 26 | "title": "The description of the Item." 27 | }, 28 | "item_type_id": { 29 | "$id": "#/properties/item_type_id", 30 | "type": "number", 31 | "title": "The id of the item type." 32 | }, 33 | "user_id": { 34 | "$id": "#/properties/user_id", 35 | "type": "number", 36 | "title": "The id of the user." 37 | }, 38 | "company_id": { 39 | "$id": "#/properties/company_id", 40 | "type": "number", 41 | "title": "The id of the company." 42 | } 43 | } 44 | } -------------------------------------------------------------------------------- /backend/src/api/items/items.test.js: -------------------------------------------------------------------------------- 1 | const supertest = require('supertest'); 2 | 3 | const app = require('../../app'); 4 | 5 | describe('GET /api/v1/items', () => { 6 | it('should respond with an array of items', async () => { 7 | const response = await supertest(app) 8 | .get('/api/v1/items') 9 | .expect('Content-Type', /json/) 10 | .expect(200); 11 | 12 | expect(response.body).toBeInstanceOf(Array); 13 | }); 14 | }); 15 | -------------------------------------------------------------------------------- /backend/src/api/states/states.queries.js: -------------------------------------------------------------------------------- 1 | const db = require('../../db'); 2 | 3 | const tableNames = require('../../constants/tableNames'); 4 | 5 | const fields = ['id', 'name', 'code']; 6 | 7 | module.exports = { 8 | find() { 9 | // TODO: filter by country 10 | // TODO: join to country table 11 | return db(tableNames.state).select(fields); 12 | }, 13 | async get(id) { 14 | return db(tableNames.state) 15 | .select(fields) 16 | .where({ 17 | id, 18 | }) 19 | .first(); 20 | }, 21 | }; 22 | -------------------------------------------------------------------------------- /backend/src/api/states/states.routes.js: -------------------------------------------------------------------------------- 1 | const express = require('express'); 2 | 3 | const queries = require('./states.queries'); 4 | 5 | const router = express.Router(); 6 | 7 | router.get('/', async (req, res) => { 8 | const states = await queries.find(); 9 | res.json(states); 10 | }); 11 | 12 | router.get('/:id', async (req, res, next) => { 13 | const { id } = req.params; 14 | try { 15 | // TODO: should we validate the ID? 16 | const state = await queries.get(parseInt(id, 10) || 0); 17 | if (state) { 18 | return res.json(state); 19 | } 20 | return next(); 21 | } catch (error) { 22 | return next(error); 23 | } 24 | }); 25 | 26 | module.exports = router; 27 | -------------------------------------------------------------------------------- /backend/src/api/states/states.test.js: -------------------------------------------------------------------------------- 1 | const supertest = require('supertest'); 2 | 3 | const app = require('../../app'); 4 | 5 | describe('GET /api/v1/states', () => { 6 | it('should respond with an array of states', async () => { 7 | const response = await supertest(app) 8 | .get('/api/v1/states') 9 | .expect('Content-Type', /json/) 10 | .expect(200); 11 | 12 | expect(response.body.length).toBeGreaterThan(0); 13 | }); 14 | 15 | it('should respond with an individual state', async () => { 16 | const response = await supertest(app) 17 | .get('/api/v1/states/1') 18 | .expect('Content-Type', /json/) 19 | .expect(200); 20 | 21 | expect(response.body.id).toBe(1); 22 | }); 23 | 24 | it('should respond with a 404 for a not found state', async () => { 25 | await supertest(app) 26 | .get('/api/v1/states/4200') 27 | .expect('Content-Type', /json/) 28 | .expect(404); 29 | }); 30 | }); 31 | -------------------------------------------------------------------------------- /backend/src/api/users/users.model.js: -------------------------------------------------------------------------------- 1 | const { Model } = require('objection'); 2 | 3 | const tableNames = require('../../constants/tableNames'); 4 | const schema = require('./users.schema.json'); 5 | 6 | class User extends Model { 7 | static get tableName() { 8 | return tableNames.user; 9 | } 10 | 11 | static get jsonSchema() { 12 | return schema; 13 | } 14 | } 15 | 16 | module.exports = User; 17 | -------------------------------------------------------------------------------- /backend/src/api/users/users.routes.js: -------------------------------------------------------------------------------- 1 | const express = require('express'); 2 | 3 | const User = require('./users.model'); 4 | 5 | const router = express.Router(); 6 | 7 | router.get('/me', async (req, res) => { 8 | const user = await User.query() 9 | .select('id', 'email', 'name', 'created_at', 'updated_at') 10 | .where('id', req.user.id) 11 | .first(); 12 | res.json(user); 13 | }); 14 | 15 | router.get('/', async (req, res) => { 16 | const users = await User.query() 17 | .select('id', 'email', 'name', 'created_at', 'updated_at') 18 | // .where('id', req.user.id) 19 | .where('deleted_at', null); 20 | res.json(users); 21 | }); 22 | 23 | module.exports = router; 24 | -------------------------------------------------------------------------------- /backend/src/api/users/users.schema.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json-schema.org/draft-07/schema", 3 | "type": "object", 4 | "title": "User", 5 | "description": "The User", 6 | "required": [ 7 | "email", 8 | "name", 9 | "password" 10 | ], 11 | "additionalProperties": false, 12 | "properties": { 13 | "id": { 14 | "$id": "#/properties/id", 15 | "type": "integer", 16 | "title": "The database ID of the user." 17 | }, 18 | "email": { 19 | "$id": "#/properties/email", 20 | "type": "string", 21 | "title": "The users email. Must be unique.", 22 | "examples": [ 23 | "cj@null.computer" 24 | ] 25 | }, 26 | "name": { 27 | "$id": "#/properties/name", 28 | "type": "string", 29 | "title": "The users name.", 30 | "examples": [ 31 | "CJ" 32 | ] 33 | }, 34 | "password": { 35 | "$id": "#/properties/password", 36 | "type": "string", 37 | "title": "The users hashed password." 38 | }, 39 | "created_at": { 40 | "$id": "#/properties/created_at", 41 | "type": "string", 42 | "title": "The creation date of the user." 43 | }, 44 | "updated_at": { 45 | "$id": "#/properties/updated_at", 46 | "type": "string", 47 | "title": "The date the user was last updated." 48 | } 49 | } 50 | } -------------------------------------------------------------------------------- /backend/src/api/users/users.test.js: -------------------------------------------------------------------------------- 1 | const supertest = require('supertest'); 2 | 3 | const app = require('../../app'); 4 | 5 | describe('GET /api/v1/users', () => { 6 | it('should respond with an array of users', async () => { 7 | const response = await supertest(app) 8 | .get('/api/v1/users') 9 | .expect('Content-Type', /json/) 10 | .expect(200); 11 | 12 | expect(response.body.length).toBeGreaterThan(0); 13 | }); 14 | }); 15 | -------------------------------------------------------------------------------- /backend/src/app.js: -------------------------------------------------------------------------------- 1 | const express = require('express'); 2 | const morgan = require('morgan'); 3 | const compression = require('compression'); 4 | const helmet = require('helmet'); 5 | 6 | const middlewares = require('./middlewares'); 7 | const api = require('./api'); 8 | const project = require('./constants/project'); 9 | const authMiddlewares = require('./api/auth/auth.middlewares'); 10 | 11 | const app = express(); 12 | 13 | app.use(morgan('tiny')); 14 | app.use(compression()); 15 | app.use(helmet()); 16 | app.use(express.json()); 17 | app.use(authMiddlewares.setUserFromTokenIfValid); 18 | 19 | app.get('/', (req, res) => { 20 | res.json({ 21 | message: project.message, 22 | }); 23 | }); 24 | 25 | app.use('/api/v1', api); 26 | 27 | app.use(middlewares.notFound); 28 | app.use(middlewares.errorHandler); 29 | 30 | module.exports = app; 31 | -------------------------------------------------------------------------------- /backend/src/app.test.js: -------------------------------------------------------------------------------- 1 | const supertest = require('supertest'); 2 | 3 | const app = require('./app'); 4 | const project = require('./constants/project'); 5 | 6 | describe('GET /', () => { 7 | it('should respond with a message', async () => { 8 | const response = await supertest(app) 9 | .get('/') 10 | .expect('Content-Type', /json/) 11 | .expect(200); 12 | 13 | expect(response.body.message).toEqual(project.message); 14 | }); 15 | }); 16 | -------------------------------------------------------------------------------- /backend/src/constants/countries.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs'); 2 | const path = require('path'); 3 | const Papa = require('papaparse'); 4 | 5 | const csvData = fs.readFileSync( 6 | path.join(__dirname, '..', '..', 'db', 'sources', 'countries.csv'), 7 | 'utf8' 8 | ); 9 | 10 | const countries = Papa.parse(csvData, { 11 | header: true, 12 | }); 13 | 14 | module.exports = countries.data.map(({ name, 'alpha-2': code }) => ({ 15 | name, 16 | code, 17 | })); 18 | -------------------------------------------------------------------------------- /backend/src/constants/project.js: -------------------------------------------------------------------------------- 1 | const message = '🏡📦🥫 Home Inventory API 🏡📦🥫'; 2 | 3 | module.exports = { 4 | message, 5 | }; 6 | -------------------------------------------------------------------------------- /backend/src/constants/tableNames.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | user: 'user', 3 | item_type: 'item_type', 4 | state: 'state', 5 | country: 'country', 6 | shape: 'shape', 7 | inventory_location: 'inventory_location', 8 | address: 'address', 9 | company: 'company', 10 | size: 'size', 11 | item: 'item', 12 | item_info: 'item_info', 13 | item_image: 'item_image', 14 | related_item: 'related_item', 15 | }; 16 | -------------------------------------------------------------------------------- /backend/src/constants/us_states.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs'); 2 | const path = require('path'); 3 | const Papa = require('papaparse'); 4 | 5 | const csvData = fs.readFileSync( 6 | path.join(__dirname, '..', '..', 'db', 'sources', 'us_states.csv'), 7 | 'utf8' 8 | ); 9 | 10 | const states = Papa.parse(csvData, { 11 | header: true, 12 | }); 13 | 14 | module.exports = states.data.map(({ state: name, code }) => ({ 15 | name, 16 | code, 17 | })); 18 | -------------------------------------------------------------------------------- /backend/src/db.js: -------------------------------------------------------------------------------- 1 | const knex = require('knex'); 2 | const { Model } = require('objection'); 3 | 4 | const knexConfig = require('../knexfile'); 5 | 6 | const environment = process.env.NODE_ENV || 'development'; 7 | const connectionConfig = knexConfig[environment]; 8 | 9 | /** 10 | * @type {import('knex').Knex} knex 11 | */ 12 | const connection = knex(connectionConfig); 13 | 14 | Model.knex(connection); 15 | 16 | module.exports = connection; 17 | -------------------------------------------------------------------------------- /backend/src/index.js: -------------------------------------------------------------------------------- 1 | const app = require('./app'); 2 | const logger = require('./lib/logger'); 3 | 4 | const port = process.env.PORT || 5050; 5 | app.listen(port, () => { 6 | logger.info(`Listening at http://localhost:${port}`); 7 | }); 8 | -------------------------------------------------------------------------------- /backend/src/lib/jwt.js: -------------------------------------------------------------------------------- 1 | const jwt = require('jsonwebtoken'); 2 | 3 | function verify(token) { 4 | return new Promise((resolve, reject) => { 5 | jwt.verify(token, process.env.JWT_SECRET, (error, decoded) => { 6 | if (error) return reject(error); 7 | return resolve(decoded); 8 | }); 9 | }); 10 | } 11 | 12 | function sign(payload) { 13 | return new Promise((resolve, reject) => { 14 | jwt.sign( 15 | payload, 16 | process.env.JWT_SECRET, 17 | { 18 | // lower expiration for privileged users... 19 | // need refresh token... 20 | expiresIn: '1m', 21 | }, 22 | (error, token) => { 23 | if (error) return reject(error); 24 | return resolve(token); 25 | } 26 | ); 27 | }); 28 | } 29 | 30 | module.exports = { 31 | verify, 32 | sign, 33 | }; 34 | -------------------------------------------------------------------------------- /backend/src/lib/logger.js: -------------------------------------------------------------------------------- 1 | const util = require('util'); 2 | 3 | const createLogger = 4 | (type) => 5 | (...args) => { 6 | // eslint-disable-next-line no-console 7 | console[type]( 8 | ...args.map((item) => { 9 | if (typeof item === 'object') { 10 | return util.inspect(item, { depth: 5, colors: true }); 11 | } 12 | return item; 13 | }) 14 | ); 15 | }; 16 | 17 | module.exports = { 18 | info: createLogger('log'), 19 | warn: createLogger('warning'), 20 | error: createLogger('error'), 21 | }; 22 | -------------------------------------------------------------------------------- /backend/src/lib/tableUtils.js: -------------------------------------------------------------------------------- 1 | function addDefaultColumns(table) { 2 | table.timestamps(false, true); 3 | table.datetime('deleted_at'); 4 | } 5 | 6 | function createNameTable(knex, table_name) { 7 | return knex.schema.createTable(table_name, (table) => { 8 | table.increments().notNullable(); 9 | table.string('name').notNullable().unique(); 10 | addDefaultColumns(table); 11 | }); 12 | } 13 | 14 | function url(table, columnName) { 15 | table.string(columnName, 2000); 16 | } 17 | 18 | function email(table, columnName) { 19 | return table.string(columnName, 254); 20 | } 21 | 22 | function references(table, tableName, notNullable = true, columnName = '') { 23 | const definition = table 24 | .integer(`${columnName || tableName}_id`) 25 | .unsigned() 26 | .references('id') 27 | .inTable(tableName) 28 | .onDelete('cascade'); 29 | 30 | if (notNullable) { 31 | definition.notNullable(); 32 | } 33 | return definition; 34 | } 35 | 36 | module.exports = { 37 | addDefaultColumns, 38 | createNameTable, 39 | url, 40 | email, 41 | references, 42 | }; 43 | -------------------------------------------------------------------------------- /backend/src/middlewares.js: -------------------------------------------------------------------------------- 1 | const errorTypes = { 2 | ValidationError: 422, 3 | UniqueViolationError: 409, 4 | }; 5 | 6 | const errorMessages = { 7 | UniqueViolationError: 'Already exists.', 8 | }; 9 | 10 | function notFound(req, res, next) { 11 | const error = new Error(`Not found - ${req.originalUrl}`); 12 | res.status(404); 13 | next(error); 14 | } 15 | 16 | // eslint-disable-next-line no-unused-vars 17 | function errorHandler(error, req, res, next) { 18 | const statusCode = 19 | res.statusCode === 200 ? errorTypes[error.name] || 500 : res.statusCode; 20 | res.status(statusCode); 21 | res.json({ 22 | status: statusCode, 23 | message: errorMessages[error.name] || error.message, 24 | stack: process.env.NODE_ENV === 'production' ? '🥞' : error.stack, 25 | errors: error.errors || undefined, 26 | }); 27 | } 28 | 29 | module.exports = { 30 | notFound, 31 | errorHandler, 32 | }; 33 | -------------------------------------------------------------------------------- /backend/src/setupFilesAfterEnv.js: -------------------------------------------------------------------------------- 1 | const db = require('./db'); 2 | 3 | global.afterAll(async () => { 4 | await db.destroy(); 5 | }); 6 | -------------------------------------------------------------------------------- /backend/src/setupTests.js: -------------------------------------------------------------------------------- 1 | const db = require('./db'); 2 | 3 | module.exports = async () => { 4 | await db.migrate.rollback(); 5 | await db.migrate.latest(); 6 | await db.seed.run(); 7 | }; 8 | -------------------------------------------------------------------------------- /backend/src/teardownTests.js: -------------------------------------------------------------------------------- 1 | const db = require('./db'); 2 | 3 | module.exports = async () => { 4 | await db.destroy(); 5 | }; 6 | --------------------------------------------------------------------------------