├── Procfile ├── docs ├── api │ └── readme.txt ├── Backend1 │ ├── get.png │ ├── reset.png │ ├── create.png │ ├── delete.png │ ├── search.png │ ├── update.png │ ├── create with json and array.png │ ├── get object with json and array.png │ └── submission.txt ├── crud-readme │ ├── get.png │ ├── reset.png │ ├── create.png │ ├── delete.png │ ├── search.png │ ├── update.png │ ├── create with json and array.png │ ├── get object with json and array.png │ └── submission.txt └── MobileHybridIonicHerokuBuildpack.pdf ├── test_files ├── sql │ ├── after.sql │ ├── demo.sql │ ├── email.sql │ └── ddl.sql └── games.psql ├── .github └── ISSUE_TEMPLATE.md ├── constants.js ├── config ├── custom-environment-variables.json └── default.json ├── modules ├── sample │ ├── routes.js │ ├── controllers │ │ └── DemoController.js │ └── services │ │ └── DemoService.js ├── crud │ ├── routes.js │ ├── controllers │ │ └── CRUDController.js │ └── services │ │ └── CRUDService.js └── mail │ ├── routes.js │ ├── controllers │ └── MailController.js │ └── services │ └── MailService.js ├── .eslintrc ├── bootstrap.js ├── .gitignore ├── app-passport.js ├── passports └── passport-jwt.js ├── common ├── helper.js ├── errors.js └── logger.js ├── package.json ├── app.js ├── app-routes.js └── README.md /Procfile: -------------------------------------------------------------------------------- 1 | web: node app.js 2 | -------------------------------------------------------------------------------- /docs/api/readme.txt: -------------------------------------------------------------------------------- 1 | APIs are now stored here: https://github.com/topcoderinc/Topcoder-StarterPack_API 2 | -------------------------------------------------------------------------------- /docs/Backend1/get.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/topcoderinc/Topcoder-StarterPack_Node-Backend/HEAD/docs/Backend1/get.png -------------------------------------------------------------------------------- /docs/Backend1/reset.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/topcoderinc/Topcoder-StarterPack_Node-Backend/HEAD/docs/Backend1/reset.png -------------------------------------------------------------------------------- /docs/Backend1/create.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/topcoderinc/Topcoder-StarterPack_Node-Backend/HEAD/docs/Backend1/create.png -------------------------------------------------------------------------------- /docs/Backend1/delete.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/topcoderinc/Topcoder-StarterPack_Node-Backend/HEAD/docs/Backend1/delete.png -------------------------------------------------------------------------------- /docs/Backend1/search.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/topcoderinc/Topcoder-StarterPack_Node-Backend/HEAD/docs/Backend1/search.png -------------------------------------------------------------------------------- /docs/Backend1/update.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/topcoderinc/Topcoder-StarterPack_Node-Backend/HEAD/docs/Backend1/update.png -------------------------------------------------------------------------------- /docs/crud-readme/get.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/topcoderinc/Topcoder-StarterPack_Node-Backend/HEAD/docs/crud-readme/get.png -------------------------------------------------------------------------------- /docs/crud-readme/reset.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/topcoderinc/Topcoder-StarterPack_Node-Backend/HEAD/docs/crud-readme/reset.png -------------------------------------------------------------------------------- /docs/crud-readme/create.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/topcoderinc/Topcoder-StarterPack_Node-Backend/HEAD/docs/crud-readme/create.png -------------------------------------------------------------------------------- /docs/crud-readme/delete.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/topcoderinc/Topcoder-StarterPack_Node-Backend/HEAD/docs/crud-readme/delete.png -------------------------------------------------------------------------------- /docs/crud-readme/search.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/topcoderinc/Topcoder-StarterPack_Node-Backend/HEAD/docs/crud-readme/search.png -------------------------------------------------------------------------------- /docs/crud-readme/update.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/topcoderinc/Topcoder-StarterPack_Node-Backend/HEAD/docs/crud-readme/update.png -------------------------------------------------------------------------------- /docs/MobileHybridIonicHerokuBuildpack.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/topcoderinc/Topcoder-StarterPack_Node-Backend/HEAD/docs/MobileHybridIonicHerokuBuildpack.pdf -------------------------------------------------------------------------------- /test_files/sql/after.sql: -------------------------------------------------------------------------------- 1 | SELECT pg_catalog.setval('games_id_seq', 3115, true); 2 | 3 | ALTER TABLE ONLY games 4 | ADD CONSTRAINT games_pkey PRIMARY KEY (id); 5 | -------------------------------------------------------------------------------- /docs/Backend1/create with json and array.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/topcoderinc/Topcoder-StarterPack_Node-Backend/HEAD/docs/Backend1/create with json and array.png -------------------------------------------------------------------------------- /docs/Backend1/get object with json and array.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/topcoderinc/Topcoder-StarterPack_Node-Backend/HEAD/docs/Backend1/get object with json and array.png -------------------------------------------------------------------------------- /docs/crud-readme/create with json and array.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/topcoderinc/Topcoder-StarterPack_Node-Backend/HEAD/docs/crud-readme/create with json and array.png -------------------------------------------------------------------------------- /docs/crud-readme/get object with json and array.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/topcoderinc/Topcoder-StarterPack_Node-Backend/HEAD/docs/crud-readme/get object with json and array.png -------------------------------------------------------------------------------- /test_files/sql/demo.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE demo 2 | ( 3 | id serial NOT NULL, 4 | name character varying(5), 5 | "json" json, -- json 6 | tags integer[], -- integer array, 7 | "timestamp" timestamp , 8 | CONSTRAINT demo_pkey PRIMARY KEY (id) 9 | ) 10 | WITH ( 11 | OIDS=FALSE 12 | ); 13 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | ### Description: 2 | 3 | ### Requirements: 4 | 5 | ### Setup & Reference: 6 | 7 | ### Submissions: 8 | - Ensure good test coverage on all modules 9 | - Upload documentation for how to run your submission 10 | - Upload all your source code as a zip for review 11 | - Winner will be required to submit a pull request with their winning code. 12 | -------------------------------------------------------------------------------- /constants.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | /* 3 | * Copyright (c) 2016 TopCoder, Inc. All rights reserved. 4 | */ 5 | 6 | /** 7 | * Application constants 8 | */ 9 | 10 | const UserRole = { 11 | SUPER_ADMIN: 'super-admin', 12 | ADMIN: 'admin', 13 | USER: 'user' 14 | }; 15 | 16 | 17 | const Passports = { 18 | jwt: 'jwt-bearer' 19 | }; 20 | 21 | 22 | module.exports = { 23 | UserRole, 24 | Passports 25 | }; 26 | -------------------------------------------------------------------------------- /config/custom-environment-variables.json: -------------------------------------------------------------------------------- 1 | { 2 | "authSecret": "AUTH_SECRET", 3 | "port":"PORT", 4 | "logLevel": "LOG_LEVEL", 5 | "version": "API_VERSION", 6 | "dbConfig": { 7 | "db_url": "DATABASE_URL", 8 | "poolSize": "DB_POOL_SIZE", 9 | "poolIdleTimeout": "DB_POOL_IDLE_TIMEOUT" 10 | }, 11 | "mailConfig": { 12 | "mailgunKey": "MAILGUN_KEY", 13 | "mailgunDomain": "MAILGUN_DOMAIN" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /config/default.json: -------------------------------------------------------------------------------- 1 | { 2 | "authSecret": "secret", 3 | "port":3000, 4 | "logLevel": "debug", 5 | "version": "v1", 6 | "dbConfig": { 7 | "db_url": "postgres://test:test@localhost:5432/test", 8 | "poolSize": 2, 9 | "poolIdleTimeout": 1000 10 | }, 11 | "mailConfig": { 12 | "mailgunKey": "key-32111f1d4060712c7fbfce9be64b2994", 13 | "mailgunDomain": "sandbox9d4303fc816f40a087900785ad188fab.mailgun.org" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /modules/sample/routes.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | /* 3 | * Copyright (c) 2016 TopCoder, Inc. All rights reserved. 4 | */ 5 | 6 | /** 7 | * Contains all routes. 8 | */ 9 | 10 | const constants = require('../../constants'); 11 | const UserRole = constants.UserRole; 12 | const jwtAuth = constants.Passports.jwt; 13 | 14 | module.exports = { 15 | '/reset': { 16 | post: { 17 | auth: jwtAuth, 18 | access: [UserRole.SUPER_ADMIN], 19 | controller: 'DemoController', 20 | method: 'reset' 21 | } 22 | } 23 | }; 24 | -------------------------------------------------------------------------------- /modules/sample/controllers/DemoController.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | /* 3 | * Copyright (c) 2016 TopCoder, Inc. All rights reserved. 4 | */ 5 | 6 | /** 7 | * This controller exposes reset database REST action. 8 | */ 9 | const DemoService = require('../services/DemoService'); 10 | 11 | /** 12 | * Resets database for objects 13 | * @param req the request 14 | * @param res the response 15 | */ 16 | function* reset(req, res) { 17 | yield DemoService.reset(); 18 | res.status(200).end(); 19 | } 20 | 21 | module.exports = { 22 | reset 23 | }; 24 | -------------------------------------------------------------------------------- /docs/Backend1/submission.txt: -------------------------------------------------------------------------------- 1 | Heroku url: https://ibp-demo.herokuapp.com/ 2 | 3 | Please check codes in backend folder 4 | 5 | Please run reset request in demo folder first. 6 | 7 | 8 | I show extra sample module to show how module will work and reset database easily. 9 | 10 | I also show how to work with complex field type like json,array. 11 | 12 | I use eslint to check code quality. 13 | 14 | api.yaml if change matchCriteria from in body to in query to match real codes it will show error in http://editor.swagger.io/ 15 | This is limited with swagger 2.0 spec editor. 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /docs/crud-readme/submission.txt: -------------------------------------------------------------------------------- 1 | Heroku url: https://ibp-demo.herokuapp.com/ 2 | 3 | Please check codes in backend folder 4 | 5 | Please run reset request in demo folder first. 6 | 7 | 8 | I show extra sample module to show how module will work and reset database easily. 9 | 10 | I also show how to work with complex field type like json,array. 11 | 12 | I use eslint to check code quality. 13 | 14 | api.yaml if change matchCriteria from in body to in query to match real codes it will show error in http://editor.swagger.io/ 15 | This is limited with swagger 2.0 spec editor. 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "airbnb-base", 3 | "rules": { 4 | "no-use-before-define": 0, 5 | "strict": 0, 6 | "comma-dangle": [2, "never"], 7 | "func-names": 0, 8 | "no-underscore-dangle": 0, 9 | "prefer-rest-params": 0, 10 | "no-param-reassign": 0, 11 | "prefer-template": 0, 12 | "new-cap": 0, 13 | "global-require": 0, 14 | "consistent-return": 0, 15 | "max-len": [2, 200], 16 | "camelcase": 0 17 | }, 18 | "parserOptions": { 19 | "ecmaVersion": 6 20 | }, 21 | "globals": { 22 | "describe": true, 23 | "it": true, 24 | "before": true, 25 | "beforeEach": true, 26 | "after": true, 27 | "afterEach": true 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /test_files/sql/email.sql: -------------------------------------------------------------------------------- 1 | SET statement_timeout = 0; 2 | SET lock_timeout = 0; 3 | SET client_encoding = 'UTF8'; 4 | SET standard_conforming_strings = on; 5 | SET check_function_bodies = false; 6 | SET client_min_messages = warning; 7 | SET row_security = off; 8 | SET search_path = public, pg_catalog; 9 | 10 | SET default_tablespace = ''; 11 | 12 | SET default_with_oids = false; 13 | 14 | -- delete previous table if needed 15 | DROP TABLE IF EXISTS emails; 16 | 17 | CREATE TABLE emails ( 18 | id serial primary key, 19 | mgid text NOT NULL, 20 | data json NOT NULL 21 | ); 22 | 23 | -- grant access if needed 24 | GRANT ALL PRIVILEGES ON TABLE emails TO PUBLIC; 25 | GRANT USAGE, SELECT ON SEQUENCE emails_id_seq TO PUBLIC; -------------------------------------------------------------------------------- /bootstrap.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | /* 3 | * Copyright (C) 2016 TopCoder Inc., All Rights Reserved. 4 | */ 5 | 6 | /** 7 | * Init app and register custom Joi type 8 | */ 9 | 10 | const Joi = require('joi'); 11 | 12 | Joi.int64 = () => Joi.number().integer() 13 | .min(-Math.pow(2, 63)) 14 | .max(Math.pow(2, 63) - 1); 15 | Joi.int32 = () => Joi.number().integer() 16 | .min(-Math.pow(2, 31)) 17 | .max(Math.pow(2, 31) - 1); 18 | Joi.objectType = () => Joi.string().required(); 19 | Joi.id = () => Joi.int64().min(1).required(); 20 | 21 | Joi.sortOrder = () => Joi.string().valid('Ascending', 'Descending'); 22 | 23 | Joi.fields = () => Joi.array().items(Joi.object().keys({ 24 | fieldName: Joi.string().required(), 25 | fieldValue: Joi.string().required() 26 | })); 27 | 28 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | *.zip 3 | # Logs 4 | logs 5 | *.log 6 | npm-debug.log* 7 | 8 | # Runtime data 9 | pids 10 | *.pid 11 | *.seed 12 | *.pid.lock 13 | 14 | # Directory for instrumented libs generated by jscoverage/JSCover 15 | lib-cov 16 | 17 | # Coverage directory used by tools like istanbul 18 | coverage 19 | 20 | # nyc test coverage 21 | .nyc_output 22 | 23 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 24 | .grunt 25 | 26 | # node-waf configuration 27 | .lock-wscript 28 | 29 | # Compiled binary addons (http://nodejs.org/api/addons.html) 30 | build/Release 31 | 32 | # Dependency directories 33 | node_modules 34 | jspm_packages 35 | 36 | # Optional npm cache directory 37 | .npm 38 | 39 | # Optional eslint cache 40 | .eslintcache 41 | 42 | # Optional REPL history 43 | .node_repl_history 44 | -------------------------------------------------------------------------------- /app-passport.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | /* 3 | * Copyright (c) 2016 TopCoder, Inc. All rights reserved. 4 | */ 5 | 6 | /** 7 | * This configuration of passport for express App. 8 | */ 9 | const glob = require('glob'); 10 | const path = require('path'); 11 | const passport = require('passport'); 12 | 13 | /** 14 | * Configure passport for express 15 | * @param app the express app 16 | */ 17 | module.exports = (app) => { 18 | // Init passport 19 | app.use(passport.initialize()); 20 | 21 | // Serialize user 22 | passport.serializeUser((user, done) => { 23 | done(null, user); 24 | }); 25 | 26 | // Deserialize user 27 | passport.deserializeUser((obj, done) => { 28 | done(null, obj); 29 | }); 30 | 31 | 32 | // load all passport strategies in passports folder. 33 | glob.sync(path.join(__dirname, './passports/*.js')) 34 | .forEach((strategy) => require(path.resolve(strategy))(passport)); 35 | }; 36 | -------------------------------------------------------------------------------- /passports/passport-jwt.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | /* 3 | * Copyright (c) 2016 TopCoder, Inc. All rights reserved. 4 | */ 5 | 6 | /** 7 | * Register JWT passport strategy. 8 | */ 9 | const jwt = require('jsonwebtoken'); 10 | const config = require('config'); 11 | const constants = require('../constants'); 12 | const BearerStrategy = require('passport-http-bearer'); 13 | const UnauthorizedError = require('../common/errors').UnauthorizedError; 14 | 15 | /** 16 | * Export jwt passport strategy 17 | * @param passport the passport 18 | */ 19 | module.exports = (passport) => { 20 | passport.use(constants.Passports.jwt, new BearerStrategy((token, done) => { 21 | jwt.verify(token, config.authSecret, (err, decoded) => { 22 | if (err) { 23 | done(new UnauthorizedError('Failed to authenticate jwt token.', err)); 24 | } else if (decoded) { 25 | done(null, decoded); 26 | } else { 27 | done(null, false); 28 | } 29 | }); 30 | })); 31 | }; 32 | -------------------------------------------------------------------------------- /common/helper.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | /* 3 | * Copyright (c) 2016 TopCoder, Inc. All rights reserved. 4 | */ 5 | 6 | /** 7 | * This file defines helper methods 8 | */ 9 | const _ = require('lodash'); 10 | const co = require('co'); 11 | 12 | 13 | /** 14 | * Wrap generator function to standard express function 15 | * @param {Function} fn the generator function 16 | * @returns {Function} the wrapped function 17 | */ 18 | function wrapExpress(fn) { 19 | return function wrapGenerator(req, res, next) { 20 | co(fn(req, res, next)).catch(next); 21 | }; 22 | } 23 | 24 | /** 25 | * Wrap all generators from object 26 | * @param obj the object (controller exports) 27 | * @returns {Object|Array} the wrapped object 28 | */ 29 | function autoWrapExpress(obj) { 30 | if (_.isArray(obj)) { 31 | return obj.map(autoWrapExpress); 32 | } 33 | if (_.isFunction(obj)) { 34 | if (obj.constructor.name === 'GeneratorFunction') { 35 | return wrapExpress(obj); 36 | } 37 | return obj; 38 | } 39 | _.each(obj, (value, key) => { 40 | obj[key] = autoWrapExpress(value); 41 | }); 42 | return obj; 43 | } 44 | 45 | module.exports = { 46 | wrapExpress, 47 | autoWrapExpress 48 | }; 49 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "backend", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "app.js", 6 | "engines": { 7 | "node": "6.x.x" 8 | }, 9 | "scripts": { 10 | "start": "node app.js", 11 | "lint": "eslint . --ext .js || true", 12 | "lint:fix": "eslint . --ext .js --fix || true" 13 | }, 14 | "author": "", 15 | "license": "ISC", 16 | "dependencies": { 17 | "bluebird": "^3.4.0", 18 | "body-parser": "^1.15.1", 19 | "co": "^4.6.0", 20 | "config": "^1.20.1", 21 | "cors": "^2.7.1", 22 | "express": "^4.13.4", 23 | "get-parameter-names": "^0.3.0", 24 | "glob": "^7.0.3", 25 | "joi": "^8.1.0", 26 | "jsonwebtoken": "^7.0.0", 27 | "lodash": "^4.12.0", 28 | "mailgun-js": "^0.7.11", 29 | "morgan": "^1.7.0", 30 | "passport": "^0.3.2", 31 | "passport-http-bearer": "^1.0.1", 32 | "pg-promise": "^4.2.3", 33 | "winston": "^2.2.0" 34 | }, 35 | "devDependencies": { 36 | "eslint": "^2.10.2", 37 | "eslint-config-airbnb": "^9.0.1", 38 | "eslint-plugin-import": "^1.8.1", 39 | "eslint-plugin-jsx-a11y": "^1.2.2", 40 | "eslint-plugin-react": "^5.1.1" 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /modules/crud/routes.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | /* 3 | * Copyright (c) 2016 TopCoder, Inc. All rights reserved. 4 | */ 5 | 6 | /** 7 | * Contains all routes. 8 | */ 9 | 10 | const constants = require('../../constants'); 11 | const UserRole = constants.UserRole; 12 | const jwtAuth = constants.Passports.jwt; 13 | 14 | module.exports = { 15 | '/objects/:objectType': { 16 | get: { 17 | auth: jwtAuth, 18 | access: [UserRole.USER, UserRole.ADMIN, UserRole.SUPER_ADMIN], 19 | controller: 'CRUDController', 20 | method: 'search' 21 | }, 22 | post: { 23 | auth: jwtAuth, 24 | access: [UserRole.ADMIN, UserRole.SUPER_ADMIN], 25 | controller: 'CRUDController', 26 | method: 'create' 27 | } 28 | }, 29 | '/objects/:objectType/:id': { 30 | get: { 31 | auth: jwtAuth, 32 | access: [UserRole.USER, UserRole.ADMIN, UserRole.SUPER_ADMIN], 33 | controller: 'CRUDController', 34 | method: 'getOne' 35 | }, 36 | put: { 37 | auth: jwtAuth, 38 | access: [UserRole.ADMIN, UserRole.SUPER_ADMIN], 39 | controller: 'CRUDController', 40 | method: 'updateOne' 41 | }, 42 | delete: { 43 | auth: jwtAuth, 44 | access: [UserRole.ADMIN, UserRole.SUPER_ADMIN], 45 | controller: 'CRUDController', 46 | method: 'deleteOne' 47 | } 48 | } 49 | }; 50 | -------------------------------------------------------------------------------- /common/errors.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | /* 3 | * Copyright (c) 2016 TopCoder, Inc. All rights reserved. 4 | */ 5 | 6 | /** 7 | * This file defines application errors. 8 | */ 9 | 10 | const util = require('util'); 11 | 12 | /** 13 | * Helper function to create generic error object with http status code 14 | * @param {String} name the error name 15 | * @param {Number} statusCode the http status code 16 | * @returns {Function} the error constructor 17 | * @private 18 | */ 19 | function _createError(name, statusCode) { 20 | /** 21 | * The error constructor 22 | * @param {String} message the error message 23 | * @param {String} [cause] the error cause 24 | * @constructor 25 | */ 26 | function ErrorCtor(message, cause) { 27 | Error.call(this); 28 | Error.captureStackTrace(this); 29 | this.message = message || name; 30 | this.cause = cause; 31 | this.httpStatus = statusCode; 32 | } 33 | 34 | util.inherits(ErrorCtor, Error); 35 | ErrorCtor.prototype.name = name; 36 | return ErrorCtor; 37 | } 38 | 39 | module.exports = { 40 | ValidationError: _createError('ValidationError', 400), 41 | BadRequestError: _createError('BadRequestError', 400), 42 | NotFoundError: _createError('NotFoundError', 404), 43 | ForbiddenError: _createError('ForbiddenError', 403), 44 | UnauthorizedError: _createError('UnauthorizedError', 401) 45 | }; 46 | -------------------------------------------------------------------------------- /test_files/sql/ddl.sql: -------------------------------------------------------------------------------- 1 | SET statement_timeout = 0; 2 | SET lock_timeout = 0; 3 | SET client_encoding = 'UTF8'; 4 | SET standard_conforming_strings = on; 5 | SET check_function_bodies = false; 6 | SET client_min_messages = warning; 7 | SET row_security = off; 8 | SET search_path = public, pg_catalog; 9 | 10 | SET default_tablespace = ''; 11 | 12 | SET default_with_oids = false; 13 | CREATE TABLE games ( 14 | id integer NOT NULL, 15 | "gameId" text, 16 | name text, 17 | image text, 18 | thumbnail text, 19 | "minPlayers" integer, 20 | "maxPlayers" integer, 21 | "playingTime" integer, 22 | "isExpansion" boolean, 23 | "yearPublished" integer, 24 | "bggRating" double precision, 25 | "averageRating" double precision, 26 | rank integer, 27 | "numPlays" integer, 28 | rating integer, 29 | owned boolean, 30 | "preOrdered" boolean, 31 | "forTrade" boolean, 32 | "previousOwned" boolean, 33 | want boolean, 34 | "wantToPlay" boolean, 35 | "wantToBuy" boolean, 36 | "wishList" boolean, 37 | "userComment" text 38 | ); 39 | CREATE SEQUENCE games_id_seq 40 | START WITH 1 41 | INCREMENT BY 1 42 | NO MINVALUE 43 | NO MAXVALUE 44 | CACHE 1; 45 | 46 | ALTER SEQUENCE games_id_seq OWNED BY games.id; 47 | 48 | ALTER TABLE ONLY games ALTER COLUMN id SET DEFAULT nextval('games_id_seq'::regclass); 49 | -------------------------------------------------------------------------------- /modules/mail/routes.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | /* 3 | * Copyright (c) 2016 TopCoder, Inc. All rights reserved. 4 | */ 5 | 6 | /** 7 | * Contains all routes. 8 | */ 9 | 10 | const constants = require('../../constants'); 11 | const UserRole = constants.UserRole; 12 | const jwtAuth = constants.Passports.jwt; 13 | 14 | module.exports = { 15 | '/emails': { 16 | post: { 17 | auth: jwtAuth, 18 | access: [UserRole.USER, UserRole.ADMIN, UserRole.SUPER_ADMIN], 19 | controller: 'MailController', 20 | method: 'sendMail' 21 | } 22 | }, 23 | '/emails/:id/deliveryStatus': { 24 | get: { 25 | auth: jwtAuth, 26 | access: [UserRole.USER, UserRole.ADMIN, UserRole.SUPER_ADMIN], 27 | controller: 'MailController', 28 | method: 'getMailStatus' 29 | } 30 | }, 31 | '/emails/stats': { 32 | get: { 33 | auth: jwtAuth, 34 | access: [UserRole.USER, UserRole.ADMIN, UserRole.SUPER_ADMIN], 35 | controller: 'MailController', 36 | method: 'getMailStatistics' 37 | } 38 | }, 39 | '/emails/:id': { 40 | get: { 41 | auth: jwtAuth, 42 | access: [UserRole.USER, UserRole.ADMIN, UserRole.SUPER_ADMIN], 43 | controller: 'MailController', 44 | method: 'getMail' 45 | }, 46 | delete: { 47 | auth: jwtAuth, 48 | access: [UserRole.USER, UserRole.ADMIN, UserRole.SUPER_ADMIN], 49 | controller: 'MailController', 50 | method: 'deleteMail' 51 | } 52 | } 53 | }; 54 | -------------------------------------------------------------------------------- /modules/mail/controllers/MailController.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const MailService = require('../services/MailService'); 4 | 5 | /** 6 | * Send an email 7 | * @param req the request 8 | * @param res the response 9 | */ 10 | function* sendMail(req, res) { 11 | const result = yield MailService.sendMail(req.body.email); 12 | // code is only set when MailService failed to receive 200 ok from Mailgun API 13 | res.status(result.code || 200).json(result); 14 | } 15 | 16 | /** 17 | * Track an email's delivery status 18 | * @param req the request 19 | * @param res the response 20 | */ 21 | function* getMailStatus(req, res) { 22 | const result = yield MailService.getMailStatus(req.params.id); 23 | // code is only set when MailService failed to receive 200 ok from Mailgun API 24 | res.status(result.code || 200).json(result); 25 | } 26 | 27 | /** 28 | * Fetch an email 29 | * @param req the request 30 | * @param res the response 31 | */ 32 | function* getMail(req, res) { 33 | res.json(yield MailService.getMail(req.params.id)); 34 | } 35 | 36 | /** 37 | * Delete an email 38 | * @param req the request 39 | * @param res the response 40 | */ 41 | function* deleteMail(req, res) { 42 | res.json(yield MailService.deleteMail(req.params.id)); 43 | } 44 | 45 | /** 46 | * get mailgun stats for last month 47 | * @param req the request 48 | * @param res the response 49 | */ 50 | function* getMailStatistics(req, res) { 51 | res.json(yield MailService.getMailStatistics()); 52 | } 53 | 54 | module.exports = { 55 | sendMail, 56 | getMailStatus, 57 | getMail, 58 | deleteMail, 59 | getMailStatistics 60 | }; 61 | -------------------------------------------------------------------------------- /modules/crud/controllers/CRUDController.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | /* 3 | * Copyright (c) 2016 TopCoder, Inc. All rights reserved. 4 | */ 5 | 6 | /** 7 | * This controller exposes CRUD REST actions. 8 | */ 9 | const CRUDService = require('../services/CRUDService'); 10 | 11 | /** 12 | * Create an object 13 | * @param req the request 14 | * @param res the response 15 | */ 16 | function* create(req, res) { 17 | const id = yield CRUDService.create(req.params.objectType, req.body); 18 | res.status(201).json(id); 19 | } 20 | 21 | /** 22 | * Search Objects 23 | * @param req the request 24 | * @param res the response 25 | */ 26 | function* search(req, res) { 27 | const result = yield CRUDService.search(req.params.objectType, req.query); 28 | res.json(result); 29 | } 30 | 31 | 32 | /** 33 | * Update an object. 34 | * @param req the request 35 | * @param res the response 36 | */ 37 | function* updateOne(req, res) { 38 | yield CRUDService.updateOne(req.params.objectType, req.params.id, req.body); 39 | res.status(200).end(); 40 | } 41 | 42 | 43 | /** 44 | * Get an object by object type and ID. 45 | * @param req the request 46 | * @param res the response 47 | */ 48 | function* getOne(req, res) { 49 | const result = yield CRUDService.getOne(req.params.objectType, req.params.id, req.query.fieldNames); 50 | res.json(result); 51 | } 52 | 53 | 54 | /** 55 | * Delete an object by object type and ID. 56 | * @param req the request 57 | * @param res the response 58 | */ 59 | function* deleteOne(req, res) { 60 | yield CRUDService.deleteOne(req.params.objectType, req.params.id); 61 | res.status(200).end(); 62 | } 63 | 64 | module.exports = { 65 | create, 66 | search, 67 | updateOne, 68 | getOne, 69 | deleteOne 70 | }; 71 | -------------------------------------------------------------------------------- /modules/sample/services/DemoService.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | /* 3 | * Copyright (c) 2016 TopCoder, Inc. All rights reserved. 4 | */ 5 | 6 | /** 7 | * This service will provide reset database operation. 8 | */ 9 | const path = require('path'); 10 | const config = require('config'); 11 | const pgp = require('pg-promise')(); 12 | const logger = require('../../../common/logger'); 13 | const QueryFile = pgp.QueryFile; 14 | 15 | // set the pool size 16 | pgp.pg.defaults.poolSize = config.dbConfig.poolSize; 17 | pgp.pg.defaults.poolIdleTimeout = config.dbConfig.poolIdleTimeout; 18 | 19 | // the folder for sql files. 20 | const relativePath = '../../../../test_files/sql/'; 21 | 22 | /** 23 | * Build sql file 24 | * @param file the sql file 25 | * @returns {QueryFile} the query file 26 | */ 27 | function sql(file) { 28 | return new QueryFile(path.join(__dirname, relativePath + file)); 29 | } 30 | 31 | 32 | /** 33 | * Resets database for objects 34 | */ 35 | function* reset() { 36 | const db = pgp(config.dbConfig.db_url); 37 | 38 | // create games and prepare seed data. 39 | try { 40 | yield db.none('DROP TABLE games'); 41 | } catch (e) { 42 | // ignore 43 | } 44 | 45 | yield db.none(sql('ddl.sql')); 46 | 47 | const games = require('../../../../test_files/games.json'); 48 | 49 | yield db.tx((t) => { 50 | const inserts = []; 51 | games.forEach(x => { 52 | const keys = Object.keys(x).map((key) => `"${key}"`); 53 | const values = Object.keys(x).map((key) => `\${${key}}`); 54 | inserts.push(t.none(`INSERT INTO games (${keys.join(', ')}) values(${values.join(', ')})`, x)); 55 | }); 56 | return t.batch(inserts); 57 | }); 58 | 59 | yield db.one(sql('after.sql')); 60 | 61 | // create table demo 62 | try { 63 | yield db.none('DROP TABLE demo'); 64 | } catch (e) { 65 | // ignore 66 | } 67 | 68 | yield db.none(sql('demo.sql')); 69 | } 70 | 71 | module.exports = { 72 | reset 73 | }; 74 | 75 | logger.buildService(module.exports); 76 | -------------------------------------------------------------------------------- /app.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | /* 3 | * Copyright (c) 2016 TopCoder, Inc. All rights reserved. 4 | */ 5 | /** 6 | * Contains express application configurations. 7 | */ 8 | require('./bootstrap'); 9 | if (!process.env.NODE_ENV) { 10 | process.env.NODE_ENV = 'development'; 11 | } 12 | 13 | const cors = require('cors'); 14 | const config = require('config'); 15 | const express = require('express'); 16 | const app = express(); 17 | const morgan = require('morgan'); 18 | const bodyParser = require('body-parser'); 19 | const passport = require('passport'); 20 | const logger = require('./common/logger'); 21 | const _ = require('lodash'); 22 | 23 | app.use(cors()); 24 | app.use(bodyParser.json()); 25 | app.use(bodyParser.urlencoded({ extended: true })); 26 | app.use(morgan('dev')); 27 | 28 | require('./app-passport')(app); 29 | require('./app-routes')(app); 30 | app.use(passport.initialize()); 31 | 32 | 33 | // The error handler,log error and return 500 error 34 | // eslint-disable-next-line no-unused-vars 35 | app.use((err, req, res, next) => { 36 | logger.logFullError(err, req.signature || `${req.method} ${req.url}`); 37 | const errorResponse = {}; 38 | const status = (err.isJoi) ? 400 : err.httpStatus || 500; 39 | 40 | if (_.isArray(err.details)) { 41 | errorResponse.fields = _.map(err.details, 'path').join(', '); 42 | if (err.isJoi) { 43 | _.map(err.details, (e) => { 44 | if (e.message) { 45 | if (_.isUndefined(errorResponse.message)) { 46 | errorResponse.message = e.message; 47 | } else { 48 | errorResponse.message += ', ' + e.message; 49 | } 50 | } 51 | }); 52 | } 53 | } 54 | if (_.isUndefined(errorResponse.message)) { 55 | if (err.message) { 56 | errorResponse.message = err.message; 57 | } else { 58 | errorResponse.message = 'server error'; 59 | } 60 | } 61 | 62 | errorResponse.code = status; 63 | res.status(status).json(errorResponse); 64 | }); 65 | 66 | const port = config.port; 67 | app.listen(port, '0.0.0.0'); 68 | logger.info('Express server listening on port %d in %s mode', port, process.env.NODE_ENV); 69 | 70 | -------------------------------------------------------------------------------- /app-routes.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | /* 3 | * Copyright (c) 2016 TopCoder, Inc. All rights reserved. 4 | */ 5 | 6 | /** 7 | * This configuration of passport for express App. 8 | */ 9 | const _ = require('lodash'); 10 | const glob = require('glob'); 11 | const Path = require('path'); 12 | const config = require('config'); 13 | const passport = require('passport'); 14 | const ForbiddenError = require('./common/errors').ForbiddenError; 15 | const UnauthorizedError = require('./common/errors').UnauthorizedError; 16 | const helper = require('./common/helper'); 17 | /** 18 | * Configure routes for express 19 | * @param app the express app 20 | */ 21 | module.exports = (app) => { 22 | // load all routes.js in modules folder. 23 | glob.sync(Path.join(__dirname, './modules/**/routes.js')) 24 | .forEach((routes) => { 25 | /* Load all routes */ 26 | _.each(require(Path.resolve(routes)), (verbs, path) => { 27 | _.each(verbs, (def, verb) => { 28 | const controllerPath = Path.join(Path.dirname(routes), `./controllers/${def.controller}`); 29 | const method = require(controllerPath)[def.method]; 30 | if (!method) { 31 | throw new Error(`${def.method} is undefined`); 32 | } 33 | const actions = []; 34 | actions.push((req, res, next) => { 35 | req.signature = `${def.controller}#${def.method}`; 36 | next(); 37 | }); 38 | // authenticate by passport if exists auth in definition of routes. 39 | if (def.auth) { 40 | actions.push((req, res, next) => { 41 | passport.authenticate(def.auth, (err, user) => { 42 | if (err || !user) { 43 | next((err instanceof UnauthorizedError) ? err : new UnauthorizedError()); 44 | } else { 45 | req.logIn(user, (error) => { 46 | next(error); 47 | }); 48 | } 49 | })(req, res, next); 50 | }); 51 | 52 | actions.push((req, res, next) => { 53 | if (!req.user) { 54 | next(new UnauthorizedError('Action is not allowed for anonymous!')); 55 | } 56 | if (Array.isArray(def.access) && 57 | (!req.user.roles || !_.intersection(req.user.roles, def.access).length)) { 58 | next(new ForbiddenError('You are not allowed to perform this action!')); 59 | } else { 60 | next(); 61 | } 62 | }); 63 | } 64 | actions.push(method); 65 | app[verb](`/api/${config.version}${path}`, helper.autoWrapExpress(actions)); 66 | }); 67 | }); 68 | }); 69 | }; 70 | -------------------------------------------------------------------------------- /common/logger.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | /* 3 | * Copyright (C) 2016 TopCoder Inc., All Rights Reserved. 4 | */ 5 | /** 6 | * This module contains the winston logger configuration. 7 | */ 8 | 9 | const _ = require('lodash'); 10 | const winston = require('winston'); 11 | const util = require('util'); 12 | const config = require('config'); 13 | const Joi = require('joi'); 14 | const getParams = require('get-parameter-names'); 15 | const logger = new (winston.Logger)({ 16 | transports: [ 17 | new (winston.transports.Console)({ 18 | level: config.logLevel, 19 | timestamp: () => new Date().toISOString() 20 | }) 21 | ] 22 | }); 23 | 24 | /** 25 | * Log error details with signature 26 | * @param err the error 27 | * @param signature the signature 28 | */ 29 | logger.logFullError = function logFullError(err, signature) { 30 | if (!err) { 31 | return; 32 | } 33 | winston.error(`Error happend in ${signature}`); 34 | const args = Array.prototype.slice.call(arguments); 35 | args.shift(); 36 | winston.error.apply(winston, args); 37 | winston.error(util.inspect(err)); 38 | if (!err.logged) { 39 | winston.error(err.stack); 40 | err.logged = true; 41 | } 42 | }; 43 | 44 | /** 45 | * Remove invalid properties from the object and hide long arrays 46 | * @param {Object} obj the object 47 | * @returns {Object} the new object with removed properties 48 | * @private 49 | */ 50 | function _sanitizeObject(obj) { 51 | try { 52 | return JSON.parse(JSON.stringify(obj, (name, value) => { 53 | if (_.isArray(value) && value.length > 30) { 54 | return `Array(${value.length}`; 55 | } 56 | return value; 57 | })); 58 | } catch (e) { 59 | return obj; 60 | } 61 | } 62 | 63 | /** 64 | * Convert array with arguments to object 65 | * @param {Array} params the name of parameters 66 | * @param {Array} arr the array with values 67 | * @private 68 | */ 69 | function _combineObject(params, arr) { 70 | const ret = {}; 71 | _.each(arr, (arg, i) => { 72 | ret[params[i]] = arg; 73 | }); 74 | return ret; 75 | } 76 | 77 | /** 78 | * Decorate all functions of a service and log debug information if DEBUG is enabled 79 | * @param {Object} service the service 80 | */ 81 | logger.decorateWithLogging = function decorateWithLogging(service) { 82 | if (config.logLevel !== 'debug') { 83 | return; 84 | } 85 | _.each(service, (method, name) => { 86 | const params = method.params || getParams(method); 87 | service[name] = function* serviceMethodWithLogging() { 88 | logger.debug(`ENTER ${name}`); 89 | logger.debug('input arguments'); 90 | const args = Array.prototype.slice.call(arguments); 91 | logger.debug(util.inspect(_sanitizeObject(_combineObject(params, args)))); 92 | try { 93 | const result = yield* method.apply(this, arguments); 94 | logger.debug(`EXIT ${name}`); 95 | logger.debug('output arguments'); 96 | logger.debug(util.inspect(_sanitizeObject(result))); 97 | return result; 98 | } catch (e) { 99 | logger.logFullError(e, name); 100 | throw e; 101 | } 102 | }; 103 | }); 104 | }; 105 | 106 | /** 107 | * Decorate all functions of a service and validate input values 108 | * and replace input arguments with sanitized result form Joi 109 | * Service method must have a `schema` property with Joi schema 110 | * @param {Object} service the service 111 | */ 112 | logger.decorateWithValidators = function decorateWithValidators(service) { 113 | _.each(service, (method, name) => { 114 | if (!method.schema) { 115 | return; 116 | } 117 | const params = getParams(method); 118 | service[name] = function* serviceMethodWithValidation() { 119 | const args = Array.prototype.slice.call(arguments); 120 | const value = _combineObject(params, args); 121 | const normalized = yield new Promise((resolve) => { 122 | Joi.validate(value, method.schema, { abortEarly: false }, (err, val) => { 123 | if (err) { 124 | throw err; 125 | } else { 126 | resolve(val); 127 | } 128 | }); 129 | }); 130 | const newArgs = []; 131 | // Joi will normalize values 132 | // for example string number '1' to 1 133 | // if schema type is number 134 | _.each(params, (param) => { 135 | newArgs.push(normalized[param]); 136 | }); 137 | return yield method.apply(this, newArgs); 138 | }; 139 | service[name].params = params; 140 | }); 141 | }; 142 | 143 | /** 144 | * Apply logger and validation decorators 145 | * @param {Object} service the service to wrap 146 | */ 147 | logger.buildService = function buildService(service) { 148 | logger.decorateWithValidators(service); 149 | logger.decorateWithLogging(service); 150 | }; 151 | 152 | module.exports = logger; 153 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # [IBP ] Node / Heroku buildpack backend for Mailgun service 2 | - This is express backend application that is based on earlier backend which implemented all CRUD API endpoints using the provided data. 3 | - *Added functionality: using Mailgun to send/track emails.* 4 | - sample heroku backend is on http://glacial-cove-96986.herokuapp.com 5 | - added readme entities, which are neccessary for running this app in this submission are **emphasized** 6 | 7 | ## **Files added** 8 | - `IBP.email.postman_collection.json` : latest postman collection 9 | - `IBP.email.postman_environment.json` : latest postman environment 10 | - `src/modules/email/..` : email module 11 | - `test_files/sql/email.sql` : sql file to setup email module 12 | - `.eslintrc` : eslintrc, change rules if you think some are not proper 13 | - `.gitignore` : basic gitignore file for node_modules 14 | 15 | ## Dependencies 16 | - runs on node.js v5.11.1 / node.js v6 17 | - [postgres 9.5](http://www.postgresql.org) 18 | - [express 4+](http://expressjs.com/) 19 | - [heroku][https://www.heroku.com] 20 | - [eslint][http://eslint.org/] 21 | - postman chrome extension for verification https://www.getpostman.com 22 | 23 | ## **Important: Mailgun configuration** 24 | - sign up for mailgun https://mailgun.com/signup 25 | - provide email, name, password. payment info not needed yet 26 | - note: unless you add domain in mailgun configuration, you can only send to email address provided above, so for testing **you should register to mailgun and use your email address to receive emails** 27 | - after registration goto https://mailgun.com/app/account/settings 28 | - copy `Private API Key` to `config/default.json -> mailConfig.mailgunKey` (or environment variable) 29 | - key is in format `key-........` 30 | ![http://i.imgur.com/DGFWrmE.png](http://i.imgur.com/DGFWrmE.png) 31 | - goto https://mailgun.com/app/domains 32 | ![http://imgur.com/R69LQHB](http://i.imgur.com/R69LQHB.png) 33 | - copy `Domain Name` to `config/default.json -> mailConfig.mailgunDomain` (or environment variable) 34 | 35 | ## Configuration 36 | - Edit configuration in `config/default.json` and 37 | - custom environment variables names in `config/custom-environment-variables.json`, 38 | - for example it will read **DATABASE_URL** as url of database from heroku. 39 | 40 | Following variables can be configured: 41 | - `authSecret` the secret for auth 42 | - `port` the port to listen 43 | - `logLevel` the log level `debug` or `info` 44 | - `version` the version of api 45 | - `dbConfig` the configurations for database and contains url, pool size and pool idle timeout. 46 | 47 | ## Heroku deployment 48 | 49 | - You will need to install [Heroku Toolbelt](https://toolbelt.heroku.com/) for this step if you don't already have it installed. 50 | 51 | - In the main project folder, run the following commands: 52 | 53 | heroku login 54 | git init 55 | git add . 56 | git commit -m "init" 57 | heroku create 58 | heroku addons:create heroku-postgresql:hobby-dev 59 | git push heroku master 60 | heroku logs -t 61 | heroku open 62 | 63 | 64 | ## Local Deployment 65 | 66 | - for development it is very handy to have buildpack deployed to your local machine. 67 | - Please make sure to configure url of database rightly in `config/default.json` or use environment variable **DATABASE_URL**. 68 | - current default `db_url: postgres://test:test@localhost:5432/test` 69 | - so, you should have postgres running on port `5432` of your `localhost` 70 | - with database name `test` 71 | - which is given access to user `test` 72 | - who can log in with password `test` 73 | - Install dependencies `npm i` 74 | - run lint check `npm run lint` 75 | - Start app `npm start` 76 | 77 | ## Database restore (existing functionality) 78 | - Please follow this article [restore](http://www.postgresql.org/docs/9.5/static/backup-dump.html#BACKUP-DUMP-RESTORE) 79 | - or [restore on heroku](https://devcenter.heroku.com/articles/heroku-postgres-import-export) to restore dump file in `test_files\games.psql`. 80 | - I actually create sample module to reset database easily you can just configure databae url rightly, start application and run reset request in demo folder in postman. 81 | 82 | - manually restore required database dump by above guides, or **launch postman(after deployed to heroku), goto demo -> reset, click send** 83 | 84 | ## **Email Database Setup** 85 | 86 | - all email data will go to table `emails` 87 | - after you did database restore above, 88 | - execute `test_files/sql/email.sql` on heroku postgres. 89 | 90 | $ cat test_files/sql/email.sql | heroku pg:psql 91 | ---> Connecting to DATABASE_URL 92 | SET 93 | SET 94 | SET 95 | SET 96 | SET 97 | SET 98 | SET 99 | SET 100 | SET 101 | SET 102 | DROP TABLE 103 | CREATE TABLE 104 | GRANT 105 | GRANT 106 | 107 | 108 | ## Setup postman 109 | - Load postman collection: 110 | 111 | - endpoints: IBP.email.postman_collection.json 112 | - environment: IBP.email.postman_environment.json 113 | 114 | - postman environment variables: 115 | 116 | - `URL` the base API url for local testing use `http://localhost:3000` or heroku app url 117 | - `TOKEN` sample super role token 118 | - `ADMIN_TOKEN` sample admin role token 119 | - `USER_TOKEN` sample user role token 120 | - `NOROLES_TOKEN` sample user without roles token 121 | - `INVALID_TOKEN` sample invalid token 122 | - `mailId` id assigned to email, after you sucessfully sent/queued an email you'll be assigned one 123 | - `address` email address to send/receive, because mailgun doesn't support mailing to outside or mailing to many receivers unless you own a domain, this address will be used as sender/receiver. so use your address to verify a mail is sent to you. use the mail address you used in registering to mailgun 124 | - `b64image` topcoder logo encoded in base64 to test image attachment functionality 125 | 126 | ## **Email Postman verification** 127 | 128 | - added mail folder to postman collection 129 | - ![http://i.imgur.com/7Cob7hO.png](http://i.imgur.com/7Cob7hO.png) 130 | - send email 131 | - ![http://i.imgur.com/zMnqUPa.png](http://i.imgur.com/zMnqUPa.png) 132 | - get email 133 | - ![http://i.imgur.com/uXyQNmi.png](http://i.imgur.com/uXyQNmi.png) 134 | - get delivery status 135 | - ![http://i.imgur.com/cTA3E1Z.png](http://i.imgur.com/cTA3E1Z.png) 136 | - delete email (this deletes mail from heroku backend, does not cancel actual mail) 137 | 138 | ## **Email API** 139 | - check `api/emailApi.yaml` for swagger definition. 140 | - POST `/emails` endpoint to send an email 141 | - to set custom headers: 142 | 143 | 144 | "headers": [ 145 | "x-test-header:1234" <== {{headerName}}:{{headerValue}} 146 | ] 147 | 148 | 149 | - to schedule a mail to be sent in the future, set `delivery_time` in ISO 8601 date format. 150 | - for attachments, `content_bytes` should be string in base64 of original binary data. 151 | 152 | "attachments": [{ 153 | "file_name": "blank.txt", 154 | "file_type": "text", 155 | "content_bytes": "IAo=" 156 | }] 157 | 158 | - after a successful POST to `/emails` endpoint, an email ID will be assigned, sample response: 159 | 160 | 200 Ok 161 | { 162 | "result": { 163 | "success": true, 164 | "code": 200 165 | }, 166 | "id": 3 167 | } 168 | 169 | - use above `id` to track delivery status, by `/emails/{id}/deliveryStatus` endpoint. 170 | 171 | ## On Mailgun Api 172 | 173 | - If you set multiple mail receivers, their api doesn't support tracking properly 174 | - Mailgun explicitly states (https://documentation.mailgun.com/api-events.html) that their event api isn't updated right on time, and users are required to throw away the result reported from the mailgun api and repeat the request on their discretion. so, for tracking, it's possible that in some cases 1) mail is sent (it is in my inbox) 2) but their event api isn't updated. 175 | 176 | ## Additional functionality 177 | 178 | - `GET /emails/stats` endpoint for tracking Mailgun usage for past 1 month. 179 | 180 | ## Module system for future developers 181 | 182 | - structure modules in each folder, `src/modules/` 183 | - put controllers for each module in `src/modules//controllers` 184 | - put services for each module in `src/modules//services` 185 | - a module should have `routes.js` on top, `src/modules//routes.js`. declare routes to controllers in this file. 186 | - use relative imports, e.g ) `const service = require('../services/fooService');` to load services from controllers within a module, or import between services within a module. 187 | - currently existing modules: `crud`, `sample`, `mail` 188 | - how the modules are loaded: `src/app-routes.js` will glob `src/modules/*/routes.js` and load them. 189 | - remove a module from application by deleting a module directory. 190 | 191 | ## Authentication & Authorization 192 | 193 | - It will use [passport](http://passportjs.org/) and it could support social feature to login as twitter, facebook easily. 194 | - It will use `src/app-passport.js` to load passport strategies in **src/passports**, you can define `auth:{name of passport strategy}` in `routes.js` of module. 195 | - You can also define `access:[role1,role2]`, the user role name is from `src/constants.js`. 196 | - It will check Authentication & Authorization in `src/app-routes.js` to secure your APIs. 197 | -------------------------------------------------------------------------------- /modules/mail/services/MailService.js: -------------------------------------------------------------------------------- 1 | 2 | 'use strict'; 3 | 4 | // imports 5 | const _ = require('lodash'); 6 | const Joi = require('joi'); 7 | const config = require('config'); 8 | const Promise = require('bluebird'); 9 | 10 | const mailgun = require('mailgun-js')({ 11 | apiKey: config.mailConfig.mailgunKey, 12 | domain: config.mailConfig.mailgunDomain 13 | }); 14 | 15 | // local imports 16 | const logger = require('../../../common/logger'); 17 | const CRUDService = require('../../crud/services/CRUDService'); 18 | 19 | const attachment = Joi.object().keys({ 20 | file_name: Joi.string().required(), 21 | file_type: Joi.string().required(), 22 | content_bytes: Joi.binary().encoding('base64').required() 23 | }); 24 | 25 | const image = Joi.object().keys({ 26 | name: Joi.string().required(), 27 | type: Joi.string().required(), 28 | content_bytes: Joi.binary().encoding('base64').required() 29 | }); 30 | 31 | const emailSchema = Joi.object().keys({ 32 | sender: Joi.string().email().required(), 33 | recipients: Joi.array().items(Joi.string().email()).required(), 34 | cc_recipients: Joi.array().items(Joi.string().email()), 35 | bcc_recipients: Joi.array().items(Joi.string().email()), 36 | subject: Joi.string(), 37 | html_body: Joi.string(), 38 | text_body: Joi.string(), 39 | attachments: Joi.array().items(attachment), 40 | images: Joi.array().items(image), 41 | headers: Joi.array().items(Joi.string().regex(/^([^:]+?):(.+)$/)), 42 | delivery_time: Joi.date().iso() // ISO 8601 date format. 43 | }); 44 | 45 | const status = { 46 | ACCEPTED: 'accepted', 47 | DELIVERED: 'delivered', 48 | FAILED: 'failed' 49 | }; 50 | 51 | /** 52 | * toAttachment convert image/attachment to mailgun attachment format 53 | * @param {object} attach Image/Attachment, refer to emailApi.yaml 54 | * @return {object} mailgun.Attachment 55 | */ 56 | function toAttachment(attach) { 57 | return new mailgun.Attachment({ 58 | data: Buffer.from(attach.content_bytes, 'base64'), 59 | contentType: attach.file_type || attach.type, 60 | filename: attach.file_name || attach.name 61 | }); 62 | } 63 | 64 | /** 65 | * send email using mailgun API 66 | * @param {object} email Email object, refer to emailApi.yaml 67 | * @yield {object} Response/Error, refer to emailApi.yaml 68 | */ 69 | function* sendMail(email) { 70 | /* 71 | Sample Email Object 72 | { 73 | "email": { 74 | "sender": "{{address}}", 75 | "recipients": ["{{address}}"], 76 | "subject": "test email", 77 | "html_body": "
test
", 78 | "text_body": "test-test-test", 79 | "headers": ["x-test-header:1234"] 80 | } 81 | } 82 | headers should be in "headerName:headerValue" format 83 | attachment content_bytes should be base64 encoded. 84 | delivery_time in format of ISO 8601 date format 85 | */ 86 | 87 | /** @type {Object} data data to be sent to mailgun API */ 88 | const data = { 89 | from: email.sender, 90 | to: email.recipients.join(',') 91 | }; 92 | 93 | // add cc_recipients if exist 94 | if (_.has(email, 'cc_recipients')) { 95 | data.cc = email.cc_recipients.join(','); 96 | } 97 | 98 | // add bcc_recipients if exist 99 | if (_.has(email, 'bcc_recipients')) { 100 | data.bcc = email.bcc_recipients.join(','); 101 | } 102 | 103 | // add subject if exist 104 | if (_.has(email, 'subject')) { 105 | data.subject = email.subject; 106 | } 107 | 108 | // add text_body if exist 109 | if (_.has(email, 'text_body')) { 110 | data.text = email.text_body; 111 | } 112 | 113 | // add html_body if exist 114 | if (_.has(email, 'html_body')) { 115 | data.html = email.html_body; 116 | } 117 | 118 | // add attachments if exist 119 | if (_.has(email, 'attachments')) { 120 | data.attachment = _.map(email.attachments, toAttachment); 121 | } 122 | 123 | // add inline images if exist 124 | if (_.has(email, 'images')) { 125 | data.inline = _.map(email.images, toAttachment); 126 | } 127 | 128 | // add headers if exist 129 | if (_.has(email, 'headers')) { 130 | _.map(email.headers, (h) => { 131 | const match = /^([^:]+?):(.+)$/.exec(h); // split first colon 132 | if (match) { 133 | const headerName = match[1]; 134 | const headerValue = match[2]; 135 | data['h:' + headerName] = headerValue; 136 | } 137 | }); 138 | } 139 | 140 | // schedule delivery if exist 141 | let deliverytime; // because mailgun only understand timezone info in 4 digits, not like "GMT " 142 | if (_.has(email, 'delivery_time') && new Date(email.delivery_time) > _.now()) { 143 | deliverytime = new Date(email.delivery_time).toUTCString(); 144 | deliverytime = deliverytime.slice(0, deliverytime.length - 4) + ' 0000'; 145 | data['o:deliverytime'] = email.delivery_time = deliverytime; 146 | } else { 147 | // send it directly and record it. 148 | email.delivery_time = new Date().toUTCString(); 149 | } 150 | 151 | /** @type {Array} crudData - data to be saved to postgres for tracking api */ 152 | const crudData = [ 153 | { fieldName: 'data', fieldValue: JSON.stringify(email) } 154 | ]; 155 | 156 | /** @type {string} mailgunId - id returned from mailgun api call */ 157 | let mailgunId = null; 158 | /** @type {Boolean} delivered - flag for delivery status */ 159 | let delivered = false; 160 | 161 | // request mailgun to send the mail 162 | logger.info('request mailgun with: ', data); 163 | /** @type {Object} response - object that will be sent to the client */ 164 | const response = yield new Promise((resolve) => { 165 | mailgun.messages().send(data, (err, body) => { 166 | if (!err) { 167 | logger.info('response mailgun success:', body); 168 | 169 | mailgunId = body.id; 170 | delivered = true; 171 | resolve({ 172 | result: { 173 | success: true, 174 | code: 200 175 | } 176 | }); 177 | } else { 178 | logger.info('response mailgun error:', err); 179 | 180 | resolve({ 181 | code: err.statusCode, 182 | message: err.message 183 | }); 184 | } 185 | }); 186 | }); 187 | 188 | // save mail record to postgres 189 | if (delivered) { 190 | crudData.push({ fieldName: 'mgid', fieldValue: JSON.stringify(mailgunId) }); 191 | response.id = yield CRUDService.create('emails', crudData); 192 | } 193 | 194 | return response; 195 | } 196 | sendMail.schema = { 197 | email: emailSchema 198 | }; 199 | 200 | /** 201 | * track email delivery information 202 | * @param {Number} id id of email 203 | * @yield {object} DeliveryStatus/Error. refer to emailApi.yaml 204 | */ 205 | function* getMailStatus(id) { 206 | const result = yield CRUDService.getOne('emails', id, ['mgid']); 207 | const mgId = JSON.parse(result[0].fieldValue); 208 | 209 | const response = yield new Promise((resolve) => { 210 | // query mailgun for delivery information 211 | mailgun.events().get({ 212 | // mailgun message-id saved enclosed in brackets 213 | 'message-id': mgId.slice(1, mgId.length - 1) 214 | }, (err, body) => { 215 | // error response from mailgun 216 | if (err) { 217 | logger.info('response mailgun error:', err); 218 | resolve({ 219 | code: err.statusCode, 220 | message: err.message 221 | }); 222 | return; 223 | } 224 | 225 | logger.info('response mailgun success:', body); 226 | 227 | // pick latest delivery information 228 | const deliverEvent = _.chain(body.items) 229 | .sortBy('timestamp') 230 | .last() 231 | .value(); 232 | 233 | if (!deliverEvent) { 234 | resolve({ 235 | code: 404, 236 | message: 'mail id ' + id + ' not found from mailgun event logs. could be deleted from mailgun event logs.' 237 | }); 238 | return; 239 | } 240 | 241 | if (deliverEvent.event === status.DELIVERED) { 242 | resolve({ 243 | delivered: true, 244 | // because mailgun returns unix timestamp in seconds for delivery_time 245 | delivery_time: new Date(deliverEvent.timestamp * 1000).toISOString() 246 | // may or may not return delivery_status on sucessful delivery 247 | // ,delivery_status: status.DELIVERED 248 | }); 249 | } else { 250 | // not yet delivered 251 | resolve({ 252 | delivered: false, 253 | delivery_status: deliverEvent.event 254 | }); 255 | } 256 | }); 257 | }); 258 | return response; 259 | } 260 | getMailStatus.schema = { 261 | id: Joi.id().required() 262 | }; 263 | 264 | /** 265 | * getMail get mail information from database 266 | * @param {Number} id id of email 267 | * @yield {object} Email/Error. refer to emailApi.yaml 268 | */ 269 | function* getMail(id) { 270 | const result = yield CRUDService.getOne('emails', id, ['data']); 271 | const email = JSON.parse(result[0].fieldValue); 272 | return email; 273 | } 274 | getMail.schema = { 275 | id: Joi.id().required() 276 | }; 277 | 278 | /** 279 | * deleteMail delete an email 280 | * @param {Number} id id of email 281 | * @yield {object} Response/Error. refer to emailApi.yaml 282 | */ 283 | function* deleteMail(id) { 284 | yield CRUDService.deleteOne('emails', id); 285 | return { 286 | id, 287 | result: { 288 | success: true, 289 | message: `email id ${id} successfully deleted.` 290 | } 291 | }; 292 | } 293 | deleteMail.schema = { 294 | id: Joi.id().required() 295 | }; 296 | 297 | /** 298 | * get mailgun stats for the last month 299 | * @yield {object} MailgunStatistics/Error mailgun stat information refer to emailApi.yaml 300 | */ 301 | function* getMailStatistics() { 302 | const result = yield new Promise((resolve) => { 303 | mailgun.stats().list({ 304 | event: [status.ACCEPTED, status.FAILED, status.DELIVERED], 305 | duration: '1m' }, (err, body) => { 306 | // error response from mailgun 307 | if (err) { 308 | logger.info('response mailgun error:', err); 309 | resolve({ 310 | code: err.statusCode, 311 | message: err.message 312 | }); 313 | return; 314 | } 315 | 316 | logger.info('response mailgun success:', body); 317 | 318 | /** @type {array} items - get latest status for each kind of event */ 319 | const items = _.chain(body.items) 320 | .sortBy((i) => new Date(i.created_at)) 321 | .reverse() 322 | .value(); 323 | 324 | /** @type {Object} response - response to return. some attributes are optional as mailgun may not return them */ 325 | const response = {}; 326 | 327 | const delivered = _.find(items, (i) => i.event === status.DELIVERED); 328 | if (delivered) { 329 | response.delivered = delivered.total_count; 330 | } 331 | const failed = _.find(items, (i) => i.event === status.FAILED); 332 | if (failed) { 333 | response.failed = failed.total_count; 334 | } 335 | const accepted = _.find(items, (i) => i.event === status.ACCEPTED); 336 | if (accepted) { 337 | response.accepted = accepted.total_count; 338 | } 339 | 340 | resolve(response); 341 | }); 342 | }); 343 | return result; 344 | } 345 | 346 | module.exports = { 347 | sendMail, 348 | getMailStatistics, 349 | getMail, 350 | deleteMail, 351 | getMailStatus 352 | }; 353 | 354 | logger.buildService(module.exports); 355 | -------------------------------------------------------------------------------- /modules/crud/services/CRUDService.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | /* 3 | * Copyright (c) 2016 TopCoder, Inc. All rights reserved. 4 | */ 5 | 6 | /** 7 | * This service will provide CRUD operations for postgres database. 8 | */ 9 | const _ = require('lodash'); 10 | const Joi = require('joi'); 11 | const config = require('config'); 12 | const ValidationError = require('../../../common/errors').ValidationError; 13 | const NotFoundError = require('../../../common/errors').NotFoundError; 14 | const logger = require('../../../common/logger'); 15 | 16 | const pgp = require('pg-promise')(); 17 | const db = pgp(config.dbConfig.db_url); 18 | 19 | // set the pool size 20 | pgp.pg.defaults.poolSize = config.dbConfig.poolSize; 21 | pgp.pg.defaults.poolIdleTimeout = config.dbConfig.poolIdleTimeout; 22 | 23 | /** 24 | * Check exist of object by id. 25 | * @param {String} objectType the type of object 26 | * @param {Number} id the id of object 27 | * @param {Object} result the result to check 28 | * @param {bool} checkRowCount the flag to check row count 29 | * @private 30 | */ 31 | function _checkExist(objectType, id, result, checkRowCount) { 32 | if (!result || (checkRowCount && !result.rowCount)) { 33 | throw new NotFoundError(`Could not find ${objectType} by id ${id}`); 34 | } 35 | } 36 | 37 | 38 | /** 39 | * Wrap name with double-quotes to work with keyword properly 40 | * see 41 | * http://www.postgresql.org/docs/9.5/static/sql-syntax-lexical.html 42 | * @param {String} name the name to escape 43 | * @param {bool} remove the flag to remove double-quotes 44 | * @returns {String} the escaped name 45 | * @private 46 | */ 47 | function _escapeName(name, remove) { 48 | if (/".*"/.test(name)) { 49 | return remove ? name.substring(1, name.length - 1) : name; 50 | } 51 | return remove ? name : `"${name}"`; 52 | } 53 | 54 | /** 55 | * Parse parameters 56 | * @param {Array} values the array with field name/value to insert 57 | * @private 58 | */ 59 | function _getParameters(values) { 60 | return _.chain(values) 61 | .keyBy(x => _escapeName(x.fieldName, true)) 62 | .mapValues(x => { 63 | try { 64 | return JSON.parse(x.fieldValue); 65 | } catch (e) { 66 | throw new ValidationError(`Invalid json string field value for '${x.fieldName}'`, e); 67 | } 68 | }) 69 | .value(); 70 | } 71 | 72 | 73 | /** 74 | * Prepare SQL query to create an object 75 | * @param {String} tableName the table name 76 | * @param {Array} values the array with field name/value to insert 77 | * @returns {Object} the SQL query with parameters 78 | * @private 79 | */ 80 | function _createQuery(tableName, values) { 81 | const columnNames = _.chain(values).map(x => _escapeName(x.fieldName)).value(); 82 | const parameters = _getParameters(values); 83 | const paramNames = _.keys(parameters).map((key) => `\${${key}}`); 84 | return { 85 | sql: `INSERT INTO ${_escapeName(tableName)} (${columnNames.join(', ')}) values(${paramNames.join(', ')}) returning id`, 86 | parameters 87 | }; 88 | } 89 | 90 | /** 91 | * Prepare SQL query to get one object 92 | * @param {String} tableName the table name 93 | * @param {Number} id the id 94 | * @param {Array} fields the array with fields to return 95 | * @private 96 | */ 97 | function _getOneQuery(tableName, id, fields) { 98 | const columnNames = fields && fields.length > 0 ? fields.map(_escapeName).join(', ') : '*'; 99 | return { 100 | sql: `SELECT ${columnNames} FROM ${_escapeName(tableName)} WHERE id = $1`, 101 | parameters: id 102 | }; 103 | } 104 | 105 | /** 106 | * Prepare SQL query to delete an object by object type and ID 107 | * @param {String} tableName the table name 108 | * @param {Number} id the id 109 | * @private 110 | */ 111 | function _deleteOneQuery(tableName, id) { 112 | return { 113 | sql: `DELETE FROM ${_escapeName(tableName)} WHERE id = $1`, 114 | parameters: id 115 | }; 116 | } 117 | 118 | /** 119 | * Prepare SQL query to update an object 120 | * @param {String} tableName the table name 121 | * @param {Number} id the id 122 | * @param {Array} values the array with field name/value to insert 123 | * @private 124 | */ 125 | function _updateOneQuery(tableName, id, values) { 126 | const columnNames = _.chain(values).map(x => `${_escapeName(x.fieldName)}=\${${_escapeName(x.fieldName, true)}}`).value(); 127 | const parameters = _getParameters(values); 128 | parameters.id = id; 129 | return { 130 | sql: `UPDATE ${_escapeName(tableName)} SET ${columnNames.join(', ')} WHERE id = \${id}`, 131 | parameters 132 | }; 133 | } 134 | 135 | /** 136 | * The sql operators with match type 137 | */ 138 | const searchOperators = { 139 | PartialMatching: 'ILIKE', 140 | ExactMatching: '=', 141 | Greater: '>', 142 | GreaterOrEqual: '>=', 143 | Equal: '=', 144 | Less: '<', 145 | LessOrEqual: '<=' 146 | }; 147 | 148 | /** 149 | * Prepare SQL query to search objects 150 | * @param {String} tableName the table name 151 | * @param {Object} criteria the search criteria 152 | * @param {Array} columns the columns of table 153 | * @private 154 | */ 155 | function _searchQuery(tableName, criteria, columns) { 156 | criteria.matchCriteria = criteria.matchCriteria || []; 157 | const parameters = []; 158 | const filterKeys = []; 159 | // criteria could exist same field names. 160 | _.each(criteria.matchCriteria, (x) => { 161 | const fieldName = _escapeName(x.fieldName, true); 162 | const column = _.find(columns, c => c.name === fieldName); 163 | if (!column) { 164 | throw new ValidationError(`There is no such column called '${fieldName}' for '${tableName}'`); 165 | } 166 | const columnType = column.type.toLowerCase(); 167 | // http://www.postgresql.org/docs/9.5/static/datatype-character.html 168 | const isStringType = columnType === 'text' || columnType.indexOf('char') !== -1; 169 | 170 | if (isStringType && ['PartialMatching', 'ExactMatching'].indexOf(x.matchType) === -1) { 171 | throw new ValidationError(`Invalid matchType for '${x.fieldName}'`); 172 | } else if (!isStringType && ['PartialMatching', 'ExactMatching'].indexOf(x.matchType) !== -1) { 173 | throw new ValidationError(`Invalid matchType for '${x.fieldName}'`); 174 | } 175 | try { 176 | const val = JSON.parse(x.value); 177 | parameters.push(x.matchType === 'PartialMatching' ? `%${val}%` : val); 178 | filterKeys.push(`${_escapeName(x.fieldName)} ${searchOperators[x.matchType]} $${parameters.length}`); 179 | } catch (e) { 180 | throw new ValidationError(`Invalid json string field value for '${x.fieldName}'`, e); 181 | } 182 | }); 183 | 184 | let where = ''; 185 | let orderBy = ''; 186 | let limitOffset = ''; 187 | if (criteria.pageNumber) { 188 | parameters.push(criteria.pageSize); 189 | limitOffset = `LIMIT $${parameters.length} OFFSET $${parameters.length + 1}`; 190 | parameters.push((criteria.pageNumber - 1) * criteria.pageSize); 191 | } 192 | if (filterKeys.length > 0) { 193 | where = `WHERE ${filterKeys.join(' AND ')}`; 194 | } 195 | if (criteria.sortBy) { 196 | if (!_.find(columns, c => c.name === criteria.sortBy)) { 197 | throw new ValidationError(`There is no such column called '${criteria.sortBy}' for '${tableName}'`); 198 | } 199 | orderBy = `ORDER BY ${_escapeName(criteria.sortBy)} `; 200 | if (criteria.sortOrder) { 201 | orderBy += criteria.sortOrder === 'Descending' ? 'DESC' : 'ASC'; 202 | } 203 | } 204 | return { 205 | sql: `SELECT * FROM ${_escapeName(tableName)} ${where} ${orderBy} ${limitOffset}`, 206 | totalSql: `SELECT COUNT(*) AS COUNT FROM ${_escapeName(tableName)} ${where}`, 207 | parameters 208 | }; 209 | } 210 | 211 | /** 212 | * Run SQL query 213 | * @param {String} methodName the method name 214 | * @param {Array} params the sql with params 215 | * @private 216 | */ 217 | function* _runSql(methodName, params) { 218 | return yield db[methodName](params.sql, params.parameters); 219 | } 220 | 221 | /** 222 | * Create an object 223 | * @param {String} objectType the type of object 224 | * @param {Array} fields the array with field name/value to insert 225 | */ 226 | function* create(objectType, fields) { 227 | const result = yield _runSql('one', _createQuery(objectType, fields)); 228 | return result.id; 229 | } 230 | 231 | create.schema = { 232 | objectType: Joi.objectType(), 233 | fields: Joi.fields().min(1) 234 | }; 235 | 236 | 237 | /** 238 | * Search Objects 239 | * @param {String} objectType the type of object 240 | * @param {Object} query the search criteria 241 | */ 242 | function* search(objectType, query) { 243 | const columns = yield _runSql('manyOrNone', { 244 | sql: `SELECT COLUMN_NAME AS NAME, DATA_TYPE AS TYPE from INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = '${_escapeName(objectType, true)}'` 245 | }); 246 | const parmas = _searchQuery(objectType, query, columns); 247 | const result = yield _runSql('manyOrNone', parmas); 248 | parmas.sql = parmas.totalSql; 249 | const totalResult = yield _runSql('one', parmas); 250 | let totalPages = 0; 251 | if (query.pageNumber && query.pageSize) { 252 | totalPages = Math.ceil(totalResult.count / query.pageSize); 253 | } else { 254 | totalPages = totalResult.count > 0 ? 1 : 0; 255 | } 256 | return { 257 | totalRecords: Number(totalResult.count), 258 | totalPages, 259 | items: _.map(result, (x) => ({ 260 | objectType, 261 | id: x.id, 262 | fields: _.map(x, (v, k) => ({ 263 | fieldName: k, 264 | fieldValue: JSON.stringify(v) 265 | })) 266 | })) 267 | }; 268 | } 269 | 270 | search.schema = { 271 | objectType: Joi.objectType(), 272 | query: Joi.object().keys({ 273 | pageSize: Joi.int32().min(1), 274 | pageNumber: Joi.int32().min(0), 275 | sortBy: Joi.string(), 276 | sortOrder: Joi.sortOrder(), 277 | matchCriteria: Joi.array().items(Joi.object().keys({ 278 | fieldName: Joi.string().required(), 279 | value: Joi.string().required(), 280 | matchType: Joi.string().valid('PartialMatching', 'ExactMatching', 281 | 'Greater', 'GreaterOrEqual', 'Equal', 'Less', 'LessOrEqual') 282 | })).single() 283 | }) 284 | }; 285 | 286 | /** 287 | * Get an object by object type and ID. 288 | * @param {String} objectType the type of object 289 | * @param {Number} id the id 290 | * @param {Array} fields the array with fields to return 291 | */ 292 | function* getOne(objectType, id, fields) { 293 | const result = yield _runSql('oneOrNone', _getOneQuery(objectType, id, fields)); 294 | _checkExist(objectType, id, result, false); 295 | return _.map(result, (v, k) => ({ 296 | fieldName: k, 297 | fieldValue: JSON.stringify(v) 298 | })); 299 | } 300 | 301 | getOne.schema = { 302 | objectType: Joi.objectType(), 303 | id: Joi.id(), 304 | fields: Joi.array().items(Joi.string()).single() 305 | }; 306 | 307 | /** 308 | * Delete an object by object type and ID. 309 | * @param {String} objectType the type of object 310 | * @param {Number} id the id of object 311 | */ 312 | function* deleteOne(objectType, id) { 313 | const result = yield _runSql('result', _deleteOneQuery(objectType, id)); 314 | _checkExist(objectType, id, result, true); 315 | return result; 316 | } 317 | 318 | deleteOne.schema = { 319 | objectType: Joi.objectType(), 320 | id: Joi.id() 321 | }; 322 | 323 | /** 324 | * Update an object. 325 | * @param {String} objectType the type of object 326 | * @param {Number} id the id 327 | * @param {Array} fields the array with field name/value to update 328 | */ 329 | function* updateOne(objectType, id, fields) { 330 | const result = yield _runSql('result', _updateOneQuery(objectType, id, fields)); 331 | _checkExist(objectType, id, result, true); 332 | } 333 | 334 | updateOne.schema = { 335 | objectType: Joi.objectType(), 336 | id: Joi.id(), 337 | fields: Joi.fields().min(1) 338 | }; 339 | 340 | module.exports = { 341 | create, 342 | search, 343 | getOne, 344 | updateOne, 345 | deleteOne 346 | }; 347 | 348 | logger.buildService(module.exports); 349 | 350 | -------------------------------------------------------------------------------- /test_files/games.psql: -------------------------------------------------------------------------------- 1 | -- 2 | -- PostgreSQL database dump 3 | -- 4 | 5 | -- Dumped from database version 9.5.2 6 | -- Dumped by pg_dump version 9.5.0 7 | 8 | SET statement_timeout = 0; 9 | SET lock_timeout = 0; 10 | SET client_encoding = 'UTF8'; 11 | SET standard_conforming_strings = on; 12 | SET check_function_bodies = false; 13 | SET client_min_messages = warning; 14 | SET row_security = off; 15 | 16 | -- 17 | -- Name: plpgsql; Type: EXTENSION; Schema: -; Owner: 18 | -- 19 | 20 | CREATE EXTENSION IF NOT EXISTS plpgsql WITH SCHEMA pg_catalog; 21 | 22 | 23 | -- 24 | -- Name: EXTENSION plpgsql; Type: COMMENT; Schema: -; Owner: 25 | -- 26 | 27 | COMMENT ON EXTENSION plpgsql IS 'PL/pgSQL procedural language'; 28 | 29 | 30 | SET search_path = public, pg_catalog; 31 | 32 | SET default_tablespace = ''; 33 | 34 | SET default_with_oids = false; 35 | 36 | -- 37 | -- Name: games; Type: TABLE; Schema: public; Owner: uriybbylwngrsv 38 | -- 39 | 40 | CREATE TABLE games ( 41 | id integer NOT NULL, 42 | "gameId" text, 43 | name text, 44 | image text, 45 | thumbnail text, 46 | "minPlayers" integer, 47 | "maxPlayers" integer, 48 | "playingTime" integer, 49 | "isExpansion" boolean, 50 | "yearPublished" integer, 51 | "bggRating" double precision, 52 | "averageRating" double precision, 53 | rank integer, 54 | "numPlays" integer, 55 | rating integer, 56 | owned boolean, 57 | "preOrdered" boolean, 58 | "forTrade" boolean, 59 | "previousOwned" boolean, 60 | want boolean, 61 | "wantToPlay" boolean, 62 | "wantToBuy" boolean, 63 | "wishList" boolean, 64 | "userComment" text 65 | ); 66 | 67 | 68 | ALTER TABLE games OWNER TO uriybbylwngrsv; 69 | 70 | -- 71 | -- Name: games_id_seq; Type: SEQUENCE; Schema: public; Owner: uriybbylwngrsv 72 | -- 73 | 74 | CREATE SEQUENCE games_id_seq 75 | START WITH 1 76 | INCREMENT BY 1 77 | NO MINVALUE 78 | NO MAXVALUE 79 | CACHE 1; 80 | 81 | 82 | ALTER TABLE games_id_seq OWNER TO uriybbylwngrsv; 83 | 84 | -- 85 | -- Name: games_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: uriybbylwngrsv 86 | -- 87 | 88 | ALTER SEQUENCE games_id_seq OWNED BY games.id; 89 | 90 | 91 | -- 92 | -- Name: id; Type: DEFAULT; Schema: public; Owner: uriybbylwngrsv 93 | -- 94 | 95 | ALTER TABLE ONLY games ALTER COLUMN id SET DEFAULT nextval('games_id_seq'::regclass); 96 | 97 | 98 | -- 99 | -- Data for Name: games; Type: TABLE DATA; Schema: public; Owner: uriybbylwngrsv 100 | -- 101 | 102 | COPY games (id, "gameId", name, image, thumbnail, "minPlayers", "maxPlayers", "playingTime", "isExpansion", "yearPublished", "bggRating", "averageRating", rank, "numPlays", rating, owned, "preOrdered", "forTrade", "previousOwned", want, "wantToPlay", "wantToBuy", "wishList", "userComment") FROM stdin; 103 | 2609 7865 10 Days in Africa http://cf.geekdo-images.com/images/pic1229634.jpg http://cf.geekdo-images.com/images/pic1229634_t.jpg 2 4 25 f 2003 0 6.56778999999999957 1302 4 7 t f f f f f f f 104 | 2610 5867 10 Days in Europe http://cf.geekdo-images.com/images/pic1229641.jpg http://cf.geekdo-images.com/images/pic1229641_t.jpg 2 4 30 f 2002 0 6.60818999999999956 1279 2 7 t f f f f f f f 105 | 2611 64956 10 Days in the Americas http://cf.geekdo-images.com/images/pic1229649.jpg http://cf.geekdo-images.com/images/pic1229649_t.jpg 2 4 20 f 2010 0 6.65927000000000024 2154 2 7 t f f f f f f f 106 | 2612 68606 12 Realms http://cf.geekdo-images.com/images/pic2761491.jpg http://cf.geekdo-images.com/images/pic2761491_t.jpg 1 6 90 f 2010 0 5.99634 4855 0 -1 t f f f f f f f 107 | 2613 154310 12 Realms: Ancestors Legacy http://cf.geekdo-images.com/images/pic2015987.jpg http://cf.geekdo-images.com/images/pic2015987_t.jpg 1 6 60 t 2015 0 6.86249999999999982 -1 0 -1 t f f f f f f f 108 | 2614 95105 1st & Goal http://cf.geekdo-images.com/images/pic1172132.jpg http://cf.geekdo-images.com/images/pic1172132_t.jpg 2 4 150 f 2011 0 6.73087999999999997 1311 1 6 f f f t f f f f 109 | 2615 192458 51st State: Master Set http://cf.geekdo-images.com/images/pic2945432.jpg http://cf.geekdo-images.com/images/pic2945432_t.jpg 1 4 90 f 2016 0 8.27500000000000036 3648 2 9 t f f f f f f f 110 | 2616 68448 7 Wonders http://cf.geekdo-images.com/images/pic860217.jpg http://cf.geekdo-images.com/images/pic860217_t.jpg 2 7 30 f 2010 0 7.85177999999999976 25 22 9 t f f f f f f f 111 | 2617 154638 7 Wonders: Babel http://cf.geekdo-images.com/images/pic2286085.jpg http://cf.geekdo-images.com/images/pic2286085_t.jpg 2 7 45 t 2014 0 7.43083000000000027 -1 0 -1 t f f f f f f f 112 | 2618 111661 7 Wonders: Cities http://cf.geekdo-images.com/images/pic1380423.jpg http://cf.geekdo-images.com/images/pic1380423_t.jpg 2 8 30 t 2012 0 7.88877999999999968 -1 0 -1 t f f f f f f f 113 | 2619 173346 7 Wonders: Duel http://cf.geekdo-images.com/images/pic2576399.jpg http://cf.geekdo-images.com/images/pic2576399_t.jpg 2 2 30 f 2015 0 8.22175000000000011 12 7 9 t f f f f f f f 114 | 2620 140098 7 Wonders: Empires http://cf.geekdo-images.com/images/pic1678158.jpg http://cf.geekdo-images.com/images/pic1678158_t.jpg 3 8 60 t 2013 0 7.62999999999999989 -1 0 -1 f f f t f f f f 115 | 2621 92539 7 Wonders: Leaders http://cf.geekdo-images.com/images/pic1046319.jpg http://cf.geekdo-images.com/images/pic1046319_t.jpg 2 7 40 t 2011 0 7.84320000000000039 -1 0 -1 t f f f f f f f 116 | 2622 133993 7 Wonders: Wonder Pack http://cf.geekdo-images.com/images/pic1495052.jpg http://cf.geekdo-images.com/images/pic1495052_t.jpg 2 7 30 t 2013 0 7.56524000000000019 -1 0 -1 t f f f f f f f 117 | 2623 172818 Above and Below http://cf.geekdo-images.com/images/pic2398773.jpg http://cf.geekdo-images.com/images/pic2398773_t.jpg 2 4 120 f 2015 0 7.75361999999999973 240 3 8 t f f f f f f f 118 | 2624 155987 Abyss http://cf.geekdo-images.com/images/pic1965255.jpg http://cf.geekdo-images.com/images/pic1965255_t.jpg 2 4 60 f 2014 0 7.34724999999999984 302 2 7 f f f t f f f f 119 | 2625 183231 Adventure Land http://cf.geekdo-images.com/images/pic2958289.jpg http://cf.geekdo-images.com/images/pic2958289_t.jpg 2 4 45 f 2015 0 7.0828300000000004 2176 1 7 t f f f f f f f 120 | 2626 111105 Agents of SMERSH http://cf.geekdo-images.com/images/pic2295348.jpg http://cf.geekdo-images.com/images/pic2295348_t.jpg 1 4 90 f 2012 0 7.13947999999999983 846 0 -1 t f f f f f f f 121 | 2627 138559 Agents of SMERSH: Swagman's Hope http://cf.geekdo-images.com/images/pic1920453.jpg http://cf.geekdo-images.com/images/pic1920453_t.jpg 1 5 120 t 2014 0 7.51456000000000035 -1 0 -1 t f f f f f f f 122 | 2628 31260 Agricola http://cf.geekdo-images.com/images/pic259085.jpg http://cf.geekdo-images.com/images/pic259085_t.jpg 1 5 150 f 2007 0 8.09075999999999951 8 13 7 t f f f f f f f 123 | 2629 119890 Agricola: All Creatures Big and Small http://cf.geekdo-images.com/images/pic1514252.jpg http://cf.geekdo-images.com/images/pic1514252_t.jpg 2 2 30 f 2012 0 7.55855000000000032 115 3 7 t f f f f f f f 124 | 2630 145355 Agricola: All Creatures Big and Small – Even More Buildings Big and Small http://cf.geekdo-images.com/images/pic1723707.jpg http://cf.geekdo-images.com/images/pic1723707_t.jpg 2 2 30 t 2013 0 7.84426999999999985 -1 0 -1 t f f f f f f f 125 | 2631 130752 Agricola: All Creatures Big and Small – More Buildings Big and Small http://cf.geekdo-images.com/images/pic1451726.jpg http://cf.geekdo-images.com/images/pic1451726_t.jpg 2 2 30 t 2012 0 7.80194999999999972 -1 0 -1 t f f f f f f f 126 | 2632 43018 Agricola: Farmers of the Moor http://cf.geekdo-images.com/images/pic568164.jpg http://cf.geekdo-images.com/images/pic568164_t.jpg 1 5 200 t 2009 0 8.13451000000000057 -1 0 -1 t f f f f f f f 127 | 2633 103183 Agricola: World Championship Deck – 2011 http://cf.geekdo-images.com/images/pic1216379.jpg http://cf.geekdo-images.com/images/pic1216379_t.jpg 1 5 180 t 2011 0 7.94034999999999958 -1 0 -1 t f f f f f f f 128 | 2634 158973 Albion's Legacy http://cf.geekdo-images.com/images/pic2026284.jpg http://cf.geekdo-images.com/images/pic2026284_t.jpg 1 6 90 f 2015 0 7.35928000000000004 4351 0 -1 f f f t f f f f 129 | 2635 161970 Alchemists http://cf.geekdo-images.com/images/pic2241156.png http://cf.geekdo-images.com/images/pic2241156_t.png 2 4 120 f 2014 0 7.83523999999999976 57 1 7 f f f t f f f f 130 | 2636 40760 Alea Iacta Est http://cf.geekdo-images.com/images/pic1218804.jpg http://cf.geekdo-images.com/images/pic1218804_t.jpg 2 5 60 f 2009 0 6.67001000000000044 959 0 -1 f f f t f f f f 131 | 2637 6249 Alhambra http://cf.geekdo-images.com/images/pic1502118.jpg http://cf.geekdo-images.com/images/pic1502118_t.jpg 2 6 60 f 2003 0 7.04588999999999999 337 11 6 f f f t f f f f 132 | 2638 9687 Alhambra: The Vizier's Favor http://cf.geekdo-images.com/images/pic105723.jpg http://cf.geekdo-images.com/images/pic105723_t.jpg 2 6 60 t 2004 0 6.89343000000000039 -1 0 -1 t f f f f f f f 133 | 2639 48726 Alien Frontiers http://cf.geekdo-images.com/images/pic1657833.jpg http://cf.geekdo-images.com/images/pic1657833_t.jpg 2 4 90 f 2010 0 7.52981000000000034 109 4 8 t f f f f f f f 134 | 2640 93193 Alien Frontiers: Factions http://cf.geekdo-images.com/images/pic2549111.jpg http://cf.geekdo-images.com/images/pic2549111_t.jpg 2 5 60 t 2012 0 7.74735999999999958 -1 0 -1 t f f f f f f f 135 | 2641 110277 Among the Stars http://cf.geekdo-images.com/images/pic2037906.jpg http://cf.geekdo-images.com/images/pic2037906_t.jpg 2 4 30 f 2012 0 7.28115999999999985 304 3 8 t f f f f f f f 136 | 2642 139630 Among the Stars: The Ambassadors http://cf.geekdo-images.com/images/pic1801598.jpg http://cf.geekdo-images.com/images/pic1801598_t.jpg 2 4 30 t 2013 0 7.52653000000000016 -1 0 -1 t f f f f f f f 137 | 2643 147253 The Ancient World http://cf.geekdo-images.com/images/pic1951629.jpg http://cf.geekdo-images.com/images/pic1951629_t.jpg 2 4 90 f 2014 0 7.27740000000000009 798 1 7 t f f f f f f f 138 | 2644 124742 Android: Netrunner http://cf.geekdo-images.com/images/pic1324609.jpg http://cf.geekdo-images.com/images/pic1324609_t.jpg 2 2 45 f 2012 0 8.09216999999999942 14 30 7 t f f f f f f f 139 | 2645 139596 Android: Netrunner – Creation and Control http://cf.geekdo-images.com/images/pic1639594.jpg http://cf.geekdo-images.com/images/pic1639594_t.jpg 2 2 45 t 2013 0 8.39071999999999996 -1 0 -1 t f f f f f f f 140 | 2646 152314 Android: Netrunner – Honor and Profit http://cf.geekdo-images.com/images/pic1878471.jpg http://cf.geekdo-images.com/images/pic1878471_t.jpg 2 2 45 t 2014 0 8.56253000000000064 -1 0 -1 t f f f f f f f 141 | 2647 159508 AquaSphere http://cf.geekdo-images.com/images/pic2197124.jpg http://cf.geekdo-images.com/images/pic2197124_t.jpg 2 4 100 f 2014 0 7.43674000000000035 282 1 7 t f f f f f f f 142 | 2648 41933 Arctic Scavengers http://cf.geekdo-images.com/images/pic1114682.jpg http://cf.geekdo-images.com/images/pic1114682_t.jpg 2 5 60 f 2009 0 7.00305999999999962 742 3 8 t f f f f f f f 143 | 2649 42950 Arctic Scavengers: HQ http://cf.geekdo-images.com/images/pic533559.jpg http://cf.geekdo-images.com/images/pic533559_t.jpg 2 5 45 t 2011 0 7.4041699999999997 -1 0 -1 t f f f f f f f 144 | 2650 42951 Arctic Scavengers: Recon http://cf.geekdo-images.com/images/pic2431658.jpg http://cf.geekdo-images.com/images/pic2431658_t.jpg 1 5 60 t 2015 0 7.68728999999999996 -1 0 -1 t f f f f f f f 145 | 2651 69789 Ascension http://cf.geekdo-images.com/images/pic2382123.jpg http://cf.geekdo-images.com/images/pic2382123_t.jpg 1 4 30 f 2010 0 7.06350999999999996 408 10 8 t f f f f f f f 146 | 2652 145633 Ascension: Apprentice Edition http://cf.geekdo-images.com/images/pic1733803.jpg http://cf.geekdo-images.com/images/pic1733803_t.jpg 2 2 30 f 2013 0 7.20544999999999991 1358 0 -1 t f f f f f f f 147 | 2653 185123 Ascension: Dreamscape http://cf.geekdo-images.com/images/pic2691059.jpg http://cf.geekdo-images.com/images/pic2691059_t.jpg 1 4 60 f 2015 0 7.97750000000000004 3295 3 8 t f f f f f f f 148 | 2654 157026 Ascension: Realms Unraveled http://cf.geekdo-images.com/images/pic2054959.png http://cf.geekdo-images.com/images/pic2054959_t.png 1 4 30 f 2014 0 7.76827999999999985 970 0 8 t f f f f f f f Outstanding. My favorite version of Ascension. 149 | 2655 95064 Ascension: Return of the Fallen http://cf.geekdo-images.com/images/pic947575.jpg http://cf.geekdo-images.com/images/pic947575_t.jpg 1 2 30 t 2011 0 7.37957000000000019 397 0 -1 f f f t f f f f %Expands: Ascension: Chronicle of the Godslayer [69789] 150 | 2656 158976 Ascension: Year One Collector's Edition http://cf.geekdo-images.com/images/pic2422635.jpg http://cf.geekdo-images.com/images/pic2422635_t.jpg 1 6 30 f 2015 0 8.19771000000000072 -1 0 -1 t f f f f f f f 151 | 2657 149241 Assault on Doomrock http://cf.geekdo-images.com/images/pic1808352.jpg http://cf.geekdo-images.com/images/pic1808352_t.jpg 1 4 150 f 2014 0 7.36856000000000044 1317 1 7 t f f f f f f f 152 | 2658 143741 BANG! The Dice Game http://cf.geekdo-images.com/images/pic2909713.jpg http://cf.geekdo-images.com/images/pic2909713_t.jpg 3 8 15 f 2013 0 7.03308 418 0 -1 f f f t f f f f 153 | 2659 135219 The Battle of Five Armies http://cf.geekdo-images.com/images/pic1886964.jpg http://cf.geekdo-images.com/images/pic1886964_t.jpg 2 2 240 f 2014 0 8.0770300000000006 359 0 -1 t f f f f f f f 154 | 2660 37111 Battlestar Galactica http://cf.geekdo-images.com/images/pic354500.jpg http://cf.geekdo-images.com/images/pic354500_t.jpg 3 6 240 f 2008 0 7.80208999999999975 39 1 8 t f f f f f f f 155 | 2661 50750 Belfort http://cf.geekdo-images.com/images/pic1368514.jpg http://cf.geekdo-images.com/images/pic1368514_t.jpg 2 5 120 f 2011 0 7.37286000000000019 248 2 8 t f f f f f f f 156 | 2662 141320 Belfort: The Expansion Expansion http://cf.geekdo-images.com/images/pic1640305.jpg http://cf.geekdo-images.com/images/pic1640305_t.jpg 2 5 90 t 2013 0 7.64057999999999993 -1 0 -1 t f f f f f f f 157 | 2663 139897 Belle of the Ball http://cf.geekdo-images.com/images/pic1753202.jpg http://cf.geekdo-images.com/images/pic1753202_t.jpg 2 5 45 f 2014 0 6.21663999999999994 3292 0 -1 f f f t f f f f 158 | 2664 10547 Betrayal at House on the Hill http://cf.geekdo-images.com/images/pic828598.jpg http://cf.geekdo-images.com/images/pic828598_t.jpg 3 6 60 f 2004 0 7.10362999999999989 352 2 6 t f f f f f f f 159 | 2665 168435 Between Two Cities http://cf.geekdo-images.com/images/pic2326114.jpg http://cf.geekdo-images.com/images/pic2326114_t.jpg 1 7 20 f 2015 0 7.2356600000000002 427 1 8 t f f f f f f f 160 | 2666 34219 Biblios http://cf.geekdo-images.com/images/pic759154.jpg http://cf.geekdo-images.com/images/pic759154_t.jpg 2 4 30 f 2007 0 7.24561000000000011 261 5 7 t f f f f f f f 161 | 2667 164506 Biblios Dice http://cf.geekdo-images.com/images/pic2300088.jpg http://cf.geekdo-images.com/images/pic2300088_t.jpg 2 5 30 f 2015 0 6.96537000000000006 2755 1 6 t f f f f f f f 162 | 2668 171233 The Big Book of Madness http://cf.geekdo-images.com/images/pic2750172.jpg http://cf.geekdo-images.com/images/pic2750172_t.jpg 2 5 90 f 2015 0 7.34884999999999966 930 1 6 t f f f f f f f 163 | 2669 157403 Black Fleet http://cf.geekdo-images.com/images/pic1987366.jpg http://cf.geekdo-images.com/images/pic1987366_t.jpg 3 4 60 f 2014 0 7.14845000000000041 563 0 -1 f f f t f f f f 164 | 2670 170216 Blood Rage http://cf.geekdo-images.com/images/pic2439223.jpg http://cf.geekdo-images.com/images/pic2439223_t.jpg 2 4 90 f 2015 0 8.26852000000000054 20 3 8 t f f f f f f f 165 | 2671 21882 Blue Moon City http://cf.geekdo-images.com/images/pic114772.jpg http://cf.geekdo-images.com/images/pic114772_t.jpg 2 4 60 f 2006 0 7.06193999999999988 401 1 7 f f f t f f f f 166 | 2672 147154 Blue Moon Legends http://cf.geekdo-images.com/images/pic1854836.jpg http://cf.geekdo-images.com/images/pic1854836_t.jpg 2 2 30 f 2014 0 7.46506000000000025 -1 0 -1 f f f t f f f f 167 | 2673 140933 Blueprints http://cf.geekdo-images.com/images/pic1877243.jpg http://cf.geekdo-images.com/images/pic1877243_t.jpg 2 4 30 f 2013 0 6.9733900000000002 579 0 -1 f f f t f f f f 168 | 2674 142267 Bomb Squad http://cf.geekdo-images.com/images/pic2162016.png http://cf.geekdo-images.com/images/pic2162016_t.png 2 6 30 f 2013 0 6.90439999999999987 2261 0 -1 t f f f f f f f 169 | 2675 139898 Brew Crafters http://cf.geekdo-images.com/images/pic1601585.jpg http://cf.geekdo-images.com/images/pic1601585_t.jpg 2 5 90 f 2013 0 7.69798999999999989 504 1 7 t f f f f f f f 170 | 2676 172308 Broom Service http://cf.geekdo-images.com/images/pic2546156.jpg http://cf.geekdo-images.com/images/pic2546156_t.jpg 2 5 75 f 2015 0 7.31440000000000001 476 0 -1 f t f f f f f f 171 | 2677 136888 Bruges http://cf.geekdo-images.com/images/pic1652004.jpg http://cf.geekdo-images.com/images/pic1652004_t.jpg 2 4 60 f 2013 0 7.52512000000000025 144 1 7 t f f f f f f f 172 | 2678 161226 The Builders: Antiquity http://cf.geekdo-images.com/images/pic2499362.jpg http://cf.geekdo-images.com/images/pic2499362_t.jpg 2 4 30 f 2015 0 7.09077000000000002 1975 1 7 t f f f f f f f 173 | 2679 153938 Camel Up http://cf.geekdo-images.com/images/pic1918028.jpg http://cf.geekdo-images.com/images/pic1918028_t.jpg 2 8 30 f 2014 0 7.11800000000000033 344 5 7 t f f f f f f f 174 | 2680 159503 The Captain Is Dead http://cf.geekdo-images.com/images/pic2037090.png http://cf.geekdo-images.com/images/pic2037090_t.png 2 7 90 f 2014 0 7.42511999999999972 1535 3 7 t f f f f f f f 175 | 2681 163685 Captain's Wager http://cf.geekdo-images.com/images/pic2563817.png http://cf.geekdo-images.com/images/pic2563817_t.png 2 5 30 f 2015 0 6.14090000000000025 6140 0 -1 f f t t f f f f 176 | 2682 822 Carcassonne http://cf.geekdo-images.com/images/pic2337577.jpg http://cf.geekdo-images.com/images/pic2337577_t.jpg 2 5 45 f 2000 0 7.43494999999999973 120 14 7 t f f f f f f f 177 | 2683 33458 Carcassonne: Count, King & Robber http://cf.geekdo-images.com/images/pic1244090.jpg http://cf.geekdo-images.com/images/pic1244090_t.jpg 2 6 45 t 2007 0 6.76668999999999965 -1 0 -1 f f f t f f f f 178 | 2684 2993 Carcassonne: Expansion 1 – Inns & Cathedrals http://cf.geekdo-images.com/images/pic2659943.jpg http://cf.geekdo-images.com/images/pic2659943_t.jpg 2 6 60 t 2002 0 7.60017999999999994 -1 0 -1 t f f f f f f f 179 | 2685 5405 Carcassonne: Expansion 2 – Traders & Builders http://cf.geekdo-images.com/images/pic2659946.jpg http://cf.geekdo-images.com/images/pic2659946_t.jpg 2 6 60 t 2003 0 7.58962000000000003 -1 0 -1 t f f f f f f f 180 | 2686 4390 Carcassonne: Hunters and Gatherers http://cf.geekdo-images.com/images/pic2428656.png http://cf.geekdo-images.com/images/pic2428656_t.png 2 5 45 f 2002 0 7.15744999999999987 313 2 6 f f f t f f f f 181 | 2687 147303 Carcassonne: South Seas http://cf.geekdo-images.com/images/pic1917224.jpg http://cf.geekdo-images.com/images/pic1917224_t.jpg 2 5 35 f 2013 0 7.21591000000000005 645 1 6 f f f t f f f f 182 | 2688 7717 Carcassonne: The Castle http://cf.geekdo-images.com/images/pic2606367.jpg http://cf.geekdo-images.com/images/pic2606367_t.jpg 2 2 30 f 2003 0 7.08133999999999997 416 4 8 t f f f f f f f 183 | 2689 12902 Carcassonne: The City http://cf.geekdo-images.com/images/pic839808.jpg http://cf.geekdo-images.com/images/pic839808_t.jpg 2 4 45 f 2004 0 7.21138999999999974 394 1 7 f f f t f f f f 184 | 2690 18836 Carcassonne: The River II http://cf.geekdo-images.com/images/pic105976.jpg http://cf.geekdo-images.com/images/pic105976_t.jpg 2 6 60 t 2005 0 7.15226999999999968 -1 0 -1 f f f t f f f f 185 | 2691 130882 Cardline: Animals http://cf.geekdo-images.com/images/pic1714093.png http://cf.geekdo-images.com/images/pic1714093_t.png 2 8 15 f 2012 0 6.67518999999999973 1531 2 6 t f f f f f f f 186 | 2692 50381 Cards Against Humanity http://cf.geekdo-images.com/images/pic2909692.jpg http://cf.geekdo-images.com/images/pic2909692_t.jpg 4 30 30 f 2009 0 6.36413999999999991 1326 1 6 t f f f f f f f 187 | 2693 43443 Castle Panic http://cf.geekdo-images.com/images/pic496923.jpg http://cf.geekdo-images.com/images/pic496923_t.jpg 1 6 60 f 2009 0 6.72518999999999956 688 1 6 f f f t f f f f 188 | 2694 133814 Castle Panic: Crossbow promo http://cf.geekdo-images.com/images/pic1515870.jpg http://cf.geekdo-images.com/images/pic1515870_t.jpg -1 -1 -1 t 2012 0 6.39881999999999973 -1 0 -1 f f f t f f f f 189 | 2695 104590 Castle Panic: The Wizard's Tower http://cf.geekdo-images.com/images/pic1063887.jpg http://cf.geekdo-images.com/images/pic1063887_t.jpg 1 6 90 t 2011 0 7.54957000000000011 -1 0 -1 f f f t f f f f 190 | 2696 84876 The Castles of Burgundy http://cf.geekdo-images.com/images/pic1176894.jpg http://cf.geekdo-images.com/images/pic1176894_t.jpg 2 4 90 f 2011 0 8.10549999999999926 9 4 8 t f f f f f f f 191 | 2697 155426 Castles of Mad King Ludwig http://cf.geekdo-images.com/images/pic1961827.jpg http://cf.geekdo-images.com/images/pic1961827_t.jpg 1 4 90 f 2014 0 7.8219599999999998 46 11 9 t f f f f f f f 192 | 2698 174296 Castles of Mad King Ludwig: Secrets http://cf.geekdo-images.com/images/pic2452313.jpg http://cf.geekdo-images.com/images/pic2452313_t.jpg 1 4 90 t 2015 0 7.57200999999999969 -1 0 -1 t f f f f f f f 193 | 2699 13 Catan http://cf.geekdo-images.com/images/pic2419375.jpg http://cf.geekdo-images.com/images/pic2419375_t.jpg 3 4 120 f 1995 0 7.30424000000000007 199 2 7 t f f f f f f f 194 | 2700 161527 Catan: Ancient Egypt http://cf.geekdo-images.com/images/pic2228343.jpg http://cf.geekdo-images.com/images/pic2228343_t.jpg 3 4 75 f 2014 0 7.29239000000000015 2947 0 -1 f f f t f f f f 195 | 2701 67239 Catan Histories: Settlers of America – Trails to Rails http://cf.geekdo-images.com/images/pic685614.jpg http://cf.geekdo-images.com/images/pic685614_t.jpg 3 4 120 f 2010 0 6.93560999999999961 857 0 -1 f f f t f f f f 196 | 2702 129351 The Cave http://cf.geekdo-images.com/images/pic1399091.jpg http://cf.geekdo-images.com/images/pic1399091_t.jpg 2 5 60 f 2012 0 6.63532000000000011 1384 1 7 f f f t f f f f 197 | 2703 102794 Caverna: The Cave Farmers http://cf.geekdo-images.com/images/pic1790789.jpg http://cf.geekdo-images.com/images/pic1790789_t.jpg 1 7 210 f 2013 0 8.23391999999999946 5 6 9 t f f f f f f f 198 | 2704 27364 Caylus Magna Carta http://cf.geekdo-images.com/images/pic339842.jpg http://cf.geekdo-images.com/images/pic339842_t.jpg 2 4 75 f 2007 0 7.00227999999999984 491 1 7 t f f f f f f f 199 | 2705 172287 Champions of Midgard http://cf.geekdo-images.com/images/pic2869714.jpg http://cf.geekdo-images.com/images/pic2869714_t.jpg 2 4 90 f 2015 0 7.69477999999999973 405 1 7 t f f f f f f f 200 | 2706 478 Citadels http://cf.geekdo-images.com/images/pic636868.jpg http://cf.geekdo-images.com/images/pic636868_t.jpg 2 8 60 f 2000 0 7.16666000000000025 244 0 -1 f f f t f f f f 201 | 2707 123499 City of Iron http://cf.geekdo-images.com/images/pic1292441.jpg http://cf.geekdo-images.com/images/pic1292441_t.jpg 2 4 120 f 2013 0 7.42884999999999973 584 1 7 t f f f f f f f 202 | 2708 36932 Claustrophobia http://cf.geekdo-images.com/images/pic570518.jpg http://cf.geekdo-images.com/images/pic570518_t.jpg 2 2 45 f 2009 0 7.64442000000000021 126 1 8 t f f f f f f f 203 | 2709 178900 Codenames http://cf.geekdo-images.com/images/pic2582929.jpg http://cf.geekdo-images.com/images/pic2582929_t.jpg 2 8 15 f 2015 0 8.02464999999999939 17 5 9 t f f f f f f f 204 | 2710 162578 Collapse http://cf.geekdo-images.com/images/pic2088004.png http://cf.geekdo-images.com/images/pic2088004_t.png 2 4 20 f 2015 0 6.41633000000000031 7148 1 7 t f f f f f f f 205 | 2711 158899 Colt Express http://cf.geekdo-images.com/images/pic2869710.jpg http://cf.geekdo-images.com/images/pic2869710_t.jpg 2 6 40 f 2014 0 7.27289999999999992 257 1 7 t f f f f f f f 206 | 2712 118174 Compounded http://cf.geekdo-images.com/images/pic1322957.jpg http://cf.geekdo-images.com/images/pic1322957_t.jpg 2 5 90 f 2013 0 6.95261999999999958 925 1 7 t f f f f f f f 207 | 2713 163257 Compounded: Geiger Expansion http://cf.geekdo-images.com/images/pic2094101.jpg http://cf.geekdo-images.com/images/pic2094101_t.jpg 1 5 90 t 2015 0 7.38902000000000037 -1 0 -1 t f f f f f f f 208 | 2714 124361 Concordia http://cf.geekdo-images.com/images/pic1980675.jpg http://cf.geekdo-images.com/images/pic1980675_t.jpg 2 5 100 f 2013 0 7.89888999999999974 43 4 9 t f f f f f f f 209 | 2715 165023 Concordia: Britannia & Germania http://cf.geekdo-images.com/images/pic2286539.jpg http://cf.geekdo-images.com/images/pic2286539_t.jpg 2 5 100 t 2014 0 8.06276000000000082 -1 0 -1 t f f f f f f f 210 | 2716 181084 Concordia: Salsa http://cf.geekdo-images.com/images/pic2682307.jpg http://cf.geekdo-images.com/images/pic2682307_t.jpg 2 5 90 t 2015 0 8.21766000000000041 -1 0 -1 t f f f f f f f 211 | 2717 98351 Core Worlds http://cf.geekdo-images.com/images/pic1063921.jpg http://cf.geekdo-images.com/images/pic1063921_t.jpg 2 5 90 f 2011 0 7.2920499999999997 378 3 7 t f f f f f f f 212 | 2718 125099 Core Worlds: Galactic Orders http://cf.geekdo-images.com/images/pic1377006.jpg http://cf.geekdo-images.com/images/pic1377006_t.jpg 2 5 90 t 2012 0 8.05381999999999998 -1 0 -1 t f f f f f f f 213 | 2719 156867 Core Worlds: Revolution http://cf.geekdo-images.com/images/pic2059260.jpg http://cf.geekdo-images.com/images/pic2059260_t.jpg 2 5 120 t 2014 0 7.75192000000000014 -1 0 -1 t f f f f f f f 214 | 2720 39463 Cosmic Encounter http://cf.geekdo-images.com/images/pic1521633.jpg http://cf.geekdo-images.com/images/pic1521633_t.jpg 3 5 120 f 2008 0 7.61191999999999958 77 3 7 t f f f f f f f 215 | 2721 61001 Cosmic Encounter: Cosmic Incursion http://cf.geekdo-images.com/images/pic657393.jpg http://cf.geekdo-images.com/images/pic657393_t.jpg 3 6 60 t 2010 0 8.27663000000000082 -1 0 -1 t f f f f f f f 216 | 2722 140863 Council of Verona http://cf.geekdo-images.com/images/pic1727393.jpg http://cf.geekdo-images.com/images/pic1727393_t.jpg 2 5 20 f 2013 0 6.52045999999999992 1510 2 7 t f f f f f f f 217 | 2723 147827 Council of Verona: Poison Expansion http://cf.geekdo-images.com/images/pic1776648.jpg http://cf.geekdo-images.com/images/pic1776648_t.jpg 2 5 30 t 2013 0 6.8979499999999998 -1 0 -1 t f f f f f f f 218 | 2724 131357 Coup http://cf.geekdo-images.com/images/pic2016054.jpg http://cf.geekdo-images.com/images/pic2016054_t.jpg 2 6 15 f 2012 0 7.1668099999999999 272 6 6 t f f f f f f f 219 | 2725 148931 Coup: Reformation http://cf.geekdo-images.com/images/pic1978817.jpg http://cf.geekdo-images.com/images/pic1978817_t.jpg 2 10 15 t 2013 0 7.43796999999999997 -1 0 -1 t f f f f f f f 220 | 2726 521 Crokinole http://cf.geekdo-images.com/images/pic79413.jpg http://cf.geekdo-images.com/images/pic79413_t.jpg 2 4 30 f 1876 0 7.80497999999999958 53 2 7 t f f f f f f f 221 | 2727 143986 CV http://cf.geekdo-images.com/images/pic1968267.jpg http://cf.geekdo-images.com/images/pic1968267_t.jpg 2 4 60 f 2013 0 6.87429000000000023 741 2 8 t f f f f f f f 222 | 2728 111124 Dark Moon http://cf.geekdo-images.com/images/pic2320134.jpg http://cf.geekdo-images.com/images/pic2320134_t.jpg 3 7 75 f 2011 0 7.20638000000000023 686 2 8 t f f f f f f f 223 | 2729 141423 Dead Men Tell No Tales http://cf.geekdo-images.com/images/pic2545662.jpg http://cf.geekdo-images.com/images/pic2545662_t.jpg 2 5 75 f 2015 0 7.34788999999999959 1048 1 7 f f f t f f f f 224 | 2730 150376 Dead of Winter: A Crossroads Game http://cf.geekdo-images.com/images/pic3016500.jpg http://cf.geekdo-images.com/images/pic3016500_t.jpg 2 5 210 f 2014 0 7.98538000000000014 23 8 8 t f f f f f f f 225 | 2731 156129 Deception: Murder in Hong Kong http://cf.geekdo-images.com/images/pic2568916.jpg http://cf.geekdo-images.com/images/pic2568916_t.jpg 4 12 20 f 2014 0 7.62985000000000024 523 4 7 t f f f f f f f 226 | 2732 65532 Defenders of the Realm http://cf.geekdo-images.com/images/pic649329.jpg http://cf.geekdo-images.com/images/pic649329_t.jpg 1 4 90 f 2010 0 7.41769999999999996 258 0 -1 f f f t f f f f 227 | 2733 93897 Defenders of the Realm: Hero Expansion #1 http://cf.geekdo-images.com/images/pic931454.jpg http://cf.geekdo-images.com/images/pic931454_t.jpg 1 4 120 t 2011 0 7.6590600000000002 -1 0 -1 f f f t f f f f 228 | 2734 93898 Defenders of the Realm: Hero Expansion #2 http://cf.geekdo-images.com/images/pic931478.jpg http://cf.geekdo-images.com/images/pic931478_t.jpg 1 4 120 t 2011 0 7.67476999999999965 -1 0 -1 f f f t f f f f 229 | 2735 93899 Defenders of the Realm: Hero Expansion #3 http://cf.geekdo-images.com/images/pic931480.jpg http://cf.geekdo-images.com/images/pic931480_t.jpg 1 4 120 t 2011 0 7.68839000000000006 -1 0 -1 f f f t f f f f 230 | 2736 72238 Defenders of the Realm: The Dragon Expansion http://cf.geekdo-images.com/images/pic788859.jpg http://cf.geekdo-images.com/images/pic788859_t.jpg 1 6 90 t 2010 0 7.70244000000000018 -1 0 -1 f f f t f f f f 231 | 2737 104162 Descent: Journeys in the Dark (Second Edition) http://cf.geekdo-images.com/images/pic1180640.jpg http://cf.geekdo-images.com/images/pic1180640_t.jpg 2 5 120 f 2012 0 7.78903000000000034 54 5 8 t f f f f f f f 232 | 2738 176443 Descent: Journeys in the Dark (Second Edition) – Dark Elements http://cf.geekdo-images.com/images/pic2497110.jpg http://cf.geekdo-images.com/images/pic2497110_t.jpg 1 4 120 t 2015 0 8.03867999999999938 -1 0 -1 t f f f f f f f 233 | 2739 153235 Descent: Journeys in the Dark (Second Edition) – Forgotten Souls http://cf.geekdo-images.com/images/pic2202239.png http://cf.geekdo-images.com/images/pic2202239_t.png 1 4 150 t 2014 0 7.88018000000000018 -1 0 -1 t f f f f f f f 234 | 2740 137842 Descent: Journeys in the Dark (Second Edition) – Labyrinth of Ruin http://cf.geekdo-images.com/images/pic1639612.jpg http://cf.geekdo-images.com/images/pic1639612_t.jpg 2 5 90 t 2013 0 8.23353000000000002 -1 0 -1 t f f f f f f f 235 | 2741 129423 Descent: Journeys in the Dark (Second Edition) – Lair of the Wyrm http://cf.geekdo-images.com/images/pic1406139.jpg http://cf.geekdo-images.com/images/pic1406139_t.jpg 2 5 120 t 2012 0 7.89154 -1 0 -1 t f f f f f f f 236 | 2742 170959 Descent: Journeys in the Dark (Second Edition) – Nature's Ire http://cf.geekdo-images.com/images/pic2365032.jpg http://cf.geekdo-images.com/images/pic2365032_t.jpg 1 4 150 t 2014 0 8.09962999999999944 -1 0 -1 t f f f f f f f 237 | 2743 162082 Deus http://cf.geekdo-images.com/images/pic2219643.png http://cf.geekdo-images.com/images/pic2219643_t.png 2 4 90 f 2014 0 7.44416000000000011 231 2 8 t f f f f f f f 238 | 2744 152162 Diamonds http://cf.geekdo-images.com/images/pic1986481.jpg http://cf.geekdo-images.com/images/pic1986481_t.jpg 2 6 30 f 2014 0 7.05287000000000042 569 0 -1 f f f t f f f f 239 | 2745 150364 Dig Down Dwarf http://cf.geekdo-images.com/images/pic1828893.jpg http://cf.geekdo-images.com/images/pic1828893_t.jpg 2 4 15 f 2013 0 6.42842999999999964 3857 1 5 f f f t f f f f 240 | 2746 171669 Discoveries http://cf.geekdo-images.com/images/pic2571301.jpg http://cf.geekdo-images.com/images/pic2571301_t.jpg 2 4 60 f 2015 0 7.29739999999999966 495 1 7 f f f f f f f f 241 | 2747 91312 Discworld: Ankh-Morpork http://cf.geekdo-images.com/images/pic1062739.jpg http://cf.geekdo-images.com/images/pic1062739_t.jpg 2 4 60 f 2011 0 7.17018999999999984 361 0 -1 t f f f f f f f 242 | 2748 125675 Doctor Who: The Card Game http://cf.geekdo-images.com/images/pic1327331.jpg http://cf.geekdo-images.com/images/pic1327331_t.jpg 2 4 60 f 2012 0 5.87929000000000013 4244 0 -1 f f f t f f f f 243 | 2749 36218 Dominion http://cf.geekdo-images.com/images/pic394356.jpg http://cf.geekdo-images.com/images/pic394356_t.jpg 2 4 30 f 2008 0 7.73733000000000004 42 7 8 t f f f f f f f 244 | 2750 66690 Dominion: Prosperity http://cf.geekdo-images.com/images/pic1747320.jpg http://cf.geekdo-images.com/images/pic1747320_t.jpg 2 4 30 t 2010 0 8.33125000000000071 -1 0 -1 t f f f f f f f 245 | 2751 156714 Doomtown: Reloaded http://cf.geekdo-images.com/images/pic1963882.jpg http://cf.geekdo-images.com/images/pic1963882_t.jpg 2 4 30 f 2014 0 7.60728999999999989 539 2 7 f f f t f f f f 246 | 2752 172933 Dragonwood http://cf.geekdo-images.com/images/pic2401338.jpg http://cf.geekdo-images.com/images/pic2401338_t.jpg 2 4 20 f 2015 0 7.0041500000000001 2844 1 7 t f f f f f f f 247 | 2753 102548 Dungeon Fighter http://cf.geekdo-images.com/images/pic2411495.png http://cf.geekdo-images.com/images/pic2411495_t.png 1 6 45 f 2011 0 6.91704000000000008 653 1 7 t f f f f f f f 248 | 2754 45315 Dungeon Lords http://cf.geekdo-images.com/images/pic569340.jpg http://cf.geekdo-images.com/images/pic569340_t.jpg 2 4 90 f 2009 0 7.48317999999999994 128 1 7 t f f f f f f f 249 | 2755 126839 Dungeon Lords: Festival Season http://cf.geekdo-images.com/images/pic1421546.jpg http://cf.geekdo-images.com/images/pic1421546_t.jpg 2 4 120 t 2012 0 7.86882000000000037 -1 0 -1 t f f f f f f f 250 | 2756 97207 Dungeon Petz http://cf.geekdo-images.com/images/pic1103979.jpg http://cf.geekdo-images.com/images/pic1103979_t.jpg 2 4 90 f 2011 0 7.62335000000000029 101 1 6 t f f f f f f f 251 | 2757 157958 DungeonQuest http://cf.geekdo-images.com/images/pic2017396.jpg http://cf.geekdo-images.com/images/pic2017396_t.jpg 1 4 60 f 2014 0 7.26626999999999956 1416 2 6 t f f f f f f f 252 | 2758 160902 Dungeons & Dragons Dice Masters: Battle for Faerûn http://cf.geekdo-images.com/images/pic2078577.jpg http://cf.geekdo-images.com/images/pic2078577_t.jpg 2 2 60 f 2015 0 7.60939000000000032 618 7 7 t f f f f f f f 253 | 2759 155689 Dungeons & Dragons: Attack Wing http://cf.geekdo-images.com/images/pic2301154.png http://cf.geekdo-images.com/images/pic2301154_t.png 2 99 30 f 2014 0 7.65420999999999996 1532 10 8 t f f f f f f f 254 | 2760 161320 Dungeons & Dragons: Attack Wing – Aarakocra Troop Expansion Pack http://cf.geekdo-images.com/images/pic2350257.jpg http://cf.geekdo-images.com/images/pic2350257_t.jpg 2 99 30 t 2014 0 7.08333000000000013 -1 0 -1 t f f f f f f f 255 | 2761 161310 Dungeons & Dragons: Attack Wing – Black Shadow Dragon Expansion Pack http://cf.geekdo-images.com/images/pic2350162.jpg http://cf.geekdo-images.com/images/pic2350162_t.jpg 2 99 30 t 2014 0 7.39142999999999972 -1 0 -1 t f f f f f f f 256 | 2762 161308 Dungeons & Dragons: Attack Wing – Dwarven Ballista Expansion Pack http://cf.geekdo-images.com/images/pic2289032.jpg http://cf.geekdo-images.com/images/pic2289032_t.jpg 2 99 30 t 2014 0 7.70666999999999991 -1 0 -1 t f f f f f f f 257 | 2763 161305 Dungeons & Dragons: Attack Wing – Frost Giant Expansion Pack http://cf.geekdo-images.com/images/pic2289044.jpg http://cf.geekdo-images.com/images/pic2289044_t.jpg 2 99 30 t 2014 0 7.78570999999999991 -1 0 -1 t f f f f f f f 258 | 2764 161291 Dungeons & Dragons: Attack Wing – Green Dragon Expansion Pack http://cf.geekdo-images.com/images/pic2289051.jpg http://cf.geekdo-images.com/images/pic2289051_t.jpg 2 99 30 t 2014 0 7.53190999999999988 -1 0 -1 t f f f f f f f 259 | 2765 161307 Dungeons & Dragons: Attack Wing – Hobgoblin Troop Expansion Pack http://cf.geekdo-images.com/images/pic2289035.jpg http://cf.geekdo-images.com/images/pic2289035_t.jpg 2 99 30 t 2014 0 7.19688000000000017 -1 0 -1 t f f f f f f f 260 | 2766 161319 Dungeons & Dragons: Attack Wing – Movanic Deva Angel Expansion Pack http://cf.geekdo-images.com/images/pic2350256.jpg http://cf.geekdo-images.com/images/pic2350256_t.jpg 2 99 30 t 2014 0 7.69047999999999998 -1 0 -1 t f f f f f f f 261 | 2767 161306 Dungeons & Dragons: Attack Wing – Sun Elf Troop Expansion Pack http://cf.geekdo-images.com/images/pic2289038.jpg http://cf.geekdo-images.com/images/pic2289038_t.jpg 2 99 30 t 2014 0 7.12068999999999974 -1 0 -1 t f f f f f f f 262 | 2768 161292 Dungeons & Dragons: Attack Wing – Sun Elf Wizard Expansion Pack http://cf.geekdo-images.com/images/pic2289049.jpg http://cf.geekdo-images.com/images/pic2289049_t.jpg 2 99 30 t 2014 0 7.66666999999999987 -1 0 -1 t f f f f f f f 263 | 2769 169015 Dungeons & Dragons: Attack Wing – White Dragon Expansion Pack http://cf.geekdo-images.com/images/pic2491811.jpg http://cf.geekdo-images.com/images/pic2491811_t.jpg 2 99 30 t 2015 0 7.51818000000000008 -1 0 -1 t f f f f f f f 264 | 2770 161309 Dungeons & Dragons: Attack Wing – Wraith Expansion Pack http://cf.geekdo-images.com/images/pic2289028.jpg http://cf.geekdo-images.com/images/pic2289028_t.jpg 2 99 30 t 2014 0 7.53200000000000003 -1 0 -1 t f f f f f f f 265 | 2771 59946 Dungeons & Dragons: Castle Ravenloft Board Game http://cf.geekdo-images.com/images/pic660244.jpg http://cf.geekdo-images.com/images/pic660244_t.jpg 1 5 60 f 2010 0 7.00769999999999982 468 2 7 f f f t f f f f 266 | 2772 172220 Dungeons & Dragons: Temple of Elemental Evil Board Game http://cf.geekdo-images.com/images/pic2496558.jpg http://cf.geekdo-images.com/images/pic2496558_t.jpg 1 5 60 f 2015 0 7.6448400000000003 1018 2 7 t f f f f f f f 267 | 2773 91872 Dungeons & Dragons: The Legend of Drizzt Board Game http://cf.geekdo-images.com/images/pic994268.jpg http://cf.geekdo-images.com/images/pic994268_t.jpg 1 5 60 f 2011 0 7.38361000000000001 328 2 7 f f f t f f f f 268 | 2774 66356 Dungeons & Dragons: Wrath of Ashardalon Board Game http://cf.geekdo-images.com/images/pic968280.jpg http://cf.geekdo-images.com/images/pic968280_t.jpg 1 5 60 f 2011 0 7.25150000000000006 389 2 7 f f f t f f f f 269 | 2775 72125 Eclipse http://cf.geekdo-images.com/images/pic1974056.jpg http://cf.geekdo-images.com/images/pic1974056_t.jpg 2 6 200 f 2011 0 8.0504200000000008 15 2 8 t f f f f f f f 270 | 2776 125898 Eclipse: Rise of the Ancients http://cf.geekdo-images.com/images/pic1340277.jpg http://cf.geekdo-images.com/images/pic1340277_t.jpg 2 9 120 t 2012 0 8.5297199999999993 -1 0 -1 t f f f f f f f 271 | 2777 104746 Eclipse: Supernova http://cf.geekdo-images.com/images/pic2047781.jpg http://cf.geekdo-images.com/images/pic2047781_t.jpg 2 6 120 t 2011 0 7.44714999999999971 -1 0 -1 t f f f f f f f 272 | 2778 156180 Eggs and Empires http://cf.geekdo-images.com/images/pic1954419.jpg http://cf.geekdo-images.com/images/pic1954419_t.jpg 2 6 25 f 2014 0 6.92178000000000004 1597 3 8 t f f f f f f f 273 | 2779 93 El Grande http://cf.geekdo-images.com/images/pic180538.jpg http://cf.geekdo-images.com/images/pic180538_t.jpg 2 5 120 f 1995 0 7.82233999999999963 31 1 7 t f f f f f f f 274 | 2780 100423 Elder Sign http://cf.geekdo-images.com/images/pic1236119.jpg http://cf.geekdo-images.com/images/pic1236119_t.jpg 1 8 90 f 2011 0 7.00736999999999988 409 0 -1 t f f f f f f f 275 | 2781 166679 Elder Sign: Gates of Arkham http://cf.geekdo-images.com/images/pic2337383.jpg http://cf.geekdo-images.com/images/pic2337383_t.jpg 1 8 90 t 2015 0 8.0975400000000004 -1 0 -1 t f f f f f f f 276 | 2782 137479 Elder Sign: Unseen Forces http://cf.geekdo-images.com/images/pic1639617.jpg http://cf.geekdo-images.com/images/pic1639617_t.jpg 1 8 90 t 2013 0 7.68067000000000011 -1 0 -1 t f f f f f f f 277 | 2783 146021 Eldritch Horror http://cf.geekdo-images.com/images/pic1872452.jpg http://cf.geekdo-images.com/images/pic1872452_t.jpg 1 8 240 f 2013 0 7.94486000000000026 29 1 8 t f f f f f f f 278 | 2784 154842 Eldritch Horror: Forsaken Lore http://cf.geekdo-images.com/images/pic1940723.jpg http://cf.geekdo-images.com/images/pic1940723_t.jpg 1 8 180 t 2014 0 8.32790999999999926 -1 0 -1 t f f f f f f f 279 | 2785 163968 Elysium http://cf.geekdo-images.com/images/pic2837103.png http://cf.geekdo-images.com/images/pic2837103_t.png 2 4 60 f 2015 0 7.44979000000000013 250 3 8 t f f f f f f f 280 | 2786 68425 Eminent Domain http://cf.geekdo-images.com/images/pic1160506.jpg http://cf.geekdo-images.com/images/pic1160506_t.jpg 2 4 45 f 2011 0 7.15195999999999987 331 6 7 t f f f f f f f 281 | 2787 86780 Eminent Domain: Bonus Planets http://cf.geekdo-images.com/images/pic1910429.jpg http://cf.geekdo-images.com/images/pic1910429_t.jpg 2 4 45 t 2011 0 7.31038000000000032 -1 0 -1 t f f f f f f f 282 | 2788 136313 Eminent Domain: Escalation http://cf.geekdo-images.com/images/pic1613196.jpg http://cf.geekdo-images.com/images/pic1613196_t.jpg 2 5 60 t 2014 0 7.95891999999999999 -1 0 -1 t f f f f f f f 283 | 2789 154137 Eminent Domain: Escalation Bonus Pack http://cf.geekdo-images.com/images/pic1914906.jpg http://cf.geekdo-images.com/images/pic1914906_t.jpg 2 5 60 t 2013 0 7.46895000000000042 -1 0 -1 t f f f f f f f 284 | 2790 128236 Escape: Illusions http://cf.geekdo-images.com/images/pic1374177.jpg http://cf.geekdo-images.com/images/pic1374177_t.jpg 1 6 10 t 2012 0 7.51712000000000025 -1 0 -1 t f f f f f f f 285 | 2791 137405 Escape: Quest http://cf.geekdo-images.com/images/pic1727702.jpg http://cf.geekdo-images.com/images/pic1727702_t.jpg 1 5 10 t 2013 0 7.71203000000000038 -1 0 -1 t f f f f f f f 286 | 2792 113294 Escape: The Curse of the Temple http://cf.geekdo-images.com/images/pic1724684.jpg http://cf.geekdo-images.com/images/pic1724684_t.jpg 1 5 10 f 2012 0 7.14637999999999973 305 2 7 t f f f f f f f 287 | 2793 133848 Euphoria: Build a Better Dystopia http://cf.geekdo-images.com/images/pic1851141.jpg http://cf.geekdo-images.com/images/pic1851141_t.jpg 2 6 60 f 2013 0 7.39337999999999962 241 3 7 t f f f f f f f 288 | 2794 155703 Evolution http://cf.geekdo-images.com/images/pic2558560.jpg http://cf.geekdo-images.com/images/pic2558560_t.jpg 2 6 70 f 2014 0 7.32357999999999976 286 2 7 f f f t f f f f 289 | 2795 176173 Evolution: Flight http://cf.geekdo-images.com/images/pic2558554.jpg http://cf.geekdo-images.com/images/pic2558554_t.jpg 2 6 60 t 2015 0 7.47529000000000021 -1 0 -1 t f f f f f f f 290 | 2796 172225 Exploding Kittens http://cf.geekdo-images.com/images/pic2691976.png http://cf.geekdo-images.com/images/pic2691976_t.png 2 5 15 f 2015 0 5.79443000000000019 4409 0 -1 t f f f f f f f 291 | 2797 13823 Fairy Tale http://cf.geekdo-images.com/images/pic2024738.jpg http://cf.geekdo-images.com/images/pic2024738_t.jpg 2 5 30 f 2004 0 6.81313000000000013 575 3 7 t f f f f f f f 292 | 2798 159675 Fields of Arle http://cf.geekdo-images.com/images/pic2225968.png http://cf.geekdo-images.com/images/pic2225968_t.png 1 2 120 f 2014 0 8.31005000000000038 64 2 8 t f f f f f f f 293 | 2799 157354 Five Tribes http://cf.geekdo-images.com/images/pic2055255.jpg http://cf.geekdo-images.com/images/pic2055255_t.jpg 2 4 80 f 2014 0 7.84879999999999978 40 8 7 t f f f f f f f 294 | 2800 176677 Five Tribes: The Artisans of Naqala http://cf.geekdo-images.com/images/pic2496396.jpg http://cf.geekdo-images.com/images/pic2496396_t.jpg 2 4 80 t 2015 0 8.20076000000000072 -1 0 -1 t f f f f f f f 295 | 2801 100901 Flash Point: Fire Rescue http://cf.geekdo-images.com/images/pic1129370.jpg http://cf.geekdo-images.com/images/pic1129370_t.jpg 1 6 45 f 2011 0 7.32214999999999971 193 2 6 f f f t f f f f 296 | 2802 126954 Flash Point: Fire Rescue – 2nd Story http://cf.geekdo-images.com/images/pic2625250.jpg http://cf.geekdo-images.com/images/pic2625250_t.jpg 1 6 45 t 2012 0 7.61908999999999992 -1 0 -1 f f f t f f f f 297 | 2803 139766 Flash Point: Fire Rescue – Extreme Danger http://cf.geekdo-images.com/images/pic2008476.jpg http://cf.geekdo-images.com/images/pic2008476_t.jpg 1 6 45 t 2013 0 7.78920999999999975 -1 0 -1 f f f t f f f f 298 | 2804 151566 Flash Point: Fire Rescue – Veteran and Rescue Dog http://cf.geekdo-images.com/images/pic1861138.png http://cf.geekdo-images.com/images/pic1861138_t.png 1 6 45 t 2013 0 7.58706999999999976 -1 0 -1 f f f t f f f f 299 | 2805 136063 Forbidden Desert http://cf.geekdo-images.com/images/pic1528722.jpg http://cf.geekdo-images.com/images/pic1528722_t.jpg 2 5 45 f 2013 0 7.36136000000000035 176 2 7 t f f f f f f f 300 | 2806 65244 Forbidden Island http://cf.geekdo-images.com/images/pic646458.jpg http://cf.geekdo-images.com/images/pic646458_t.jpg 2 4 30 f 2010 0 6.88588999999999984 461 1 6 f f f t f f f f 301 | 2807 146221 Forge War http://cf.geekdo-images.com/images/pic2296858.jpg http://cf.geekdo-images.com/images/pic2296858_t.jpg 1 4 180 f 2015 0 7.58471999999999991 775 0 -1 f f f t f f f f 302 | 2808 119506 Freedom: The Underground Railroad http://cf.geekdo-images.com/images/pic1478723.jpg http://cf.geekdo-images.com/images/pic1478723_t.jpg 1 4 120 f 2012 0 7.66788000000000025 279 0 -1 t f f f f f f f 303 | 2809 66188 Fresco http://cf.geekdo-images.com/images/pic714398.jpg http://cf.geekdo-images.com/images/pic714398_t.jpg 2 4 60 f 2010 0 7.36043999999999965 191 2 8 t f f f f f f f 304 | 2810 86048 Fresco: Expansion Module 7 – The Scrolls http://cf.geekdo-images.com/images/pic1046431.jpg http://cf.geekdo-images.com/images/pic1046431_t.jpg 2 4 45 t 2010 0 6.85252000000000017 -1 0 -1 t f f f f f f f 305 | 2811 85925 Fresco: Expansion Modules 4, 5 and 6 http://cf.geekdo-images.com/images/pic1102113.jpg http://cf.geekdo-images.com/images/pic1102113_t.jpg 2 4 60 t 2011 0 7.49014999999999986 -1 0 -1 t f f f f f f f 306 | 2812 148289 Fresco: Expansion Modules 8, 9 and 10 http://cf.geekdo-images.com/images/pic1805672.jpg http://cf.geekdo-images.com/images/pic1805672_t.jpg 2 4 90 t 2013 0 7.52202000000000037 -1 0 -1 t f f f f f f f 307 | 2813 109140 Fresco: The Bishop's Favor http://cf.geekdo-images.com/images/pic1102403.jpg http://cf.geekdo-images.com/images/pic1102403_t.jpg 2 4 60 t 2011 0 6.98341000000000012 -1 0 -1 t f f f f f f f 308 | 2814 43570 Friday http://cf.geekdo-images.com/images/pic1513328.jpg http://cf.geekdo-images.com/images/pic1513328_t.jpg 1 1 25 f 2011 0 7.26649999999999974 264 1 7 f f f t f f f f 309 | 2815 163081 Galaxy of Trian http://cf.geekdo-images.com/images/pic2217697.jpg http://cf.geekdo-images.com/images/pic2217697_t.jpg 2 4 45 f 2014 0 6.95148999999999972 -1 1 7 t f f f f f f f 310 | 2816 143693 Glass Road http://cf.geekdo-images.com/images/pic2499302.jpg http://cf.geekdo-images.com/images/pic2499302_t.jpg 1 4 75 f 2013 0 7.51949999999999985 167 1 7 t f f f f f f f 311 | 2817 19857 Glory to Rome http://cf.geekdo-images.com/images/pic1867932.jpg http://cf.geekdo-images.com/images/pic1867932_t.jpg 2 5 60 f 2005 0 7.5106799999999998 111 7 8 t f f f f f f f 312 | 2818 188 Go http://cf.geekdo-images.com/images/pic1728832.jpg http://cf.geekdo-images.com/images/pic1728832_t.jpg 2 2 90 f -2200 0 7.69885000000000019 82 1 7 t f f f f f f f 313 | 2819 164338 The Golden Ages http://cf.geekdo-images.com/images/pic2551380.jpg http://cf.geekdo-images.com/images/pic2551380_t.jpg 2 4 90 f 2014 0 7.42229000000000028 691 3 7 t f f f f f f f 314 | 2820 182874 Grand Austria Hotel http://cf.geekdo-images.com/images/pic2728138.jpg http://cf.geekdo-images.com/images/pic2728138_t.jpg 2 4 120 f 2015 0 7.73892999999999986 330 1 7 t f f f f f f f 315 | 2821 111417 The Great Heartland Hauling Co. http://cf.geekdo-images.com/images/pic1350080.jpg http://cf.geekdo-images.com/images/pic1350080_t.jpg 2 4 30 f 2013 0 7.02590000000000003 872 0 -1 f f f t f f f f 316 | 2822 151247 Greed http://cf.geekdo-images.com/images/pic1949247.jpg http://cf.geekdo-images.com/images/pic1949247_t.jpg 2 5 30 f 2014 0 6.86878000000000011 1112 2 7 f f f t f f f f 317 | 2823 98778 Hanabi http://cf.geekdo-images.com/images/pic2007286.jpg http://cf.geekdo-images.com/images/pic2007286_t.jpg 2 5 25 f 2010 0 7.28641999999999967 181 2 7 t f f f f f f f 318 | 2824 154182 Helios http://cf.geekdo-images.com/images/pic1928217.png http://cf.geekdo-images.com/images/pic1928217_t.png 2 4 60 f 2014 0 6.90071000000000012 1152 1 7 f f f t f f f f 319 | 2825 2655 Hive http://cf.geekdo-images.com/images/pic791151.jpg http://cf.geekdo-images.com/images/pic791151_t.jpg 2 2 20 f 2001 0 7.34332000000000029 158 2 6 t f f f f f f f 320 | 2826 134253 Hostage Negotiator http://cf.geekdo-images.com/images/pic2043737.jpg http://cf.geekdo-images.com/images/pic2043737_t.jpg 1 1 20 f 2015 0 7.33917000000000019 962 0 -1 t f f f f f f f 321 | 2827 119788 Hyperborea http://cf.geekdo-images.com/images/pic2004236.jpg http://cf.geekdo-images.com/images/pic2004236_t.jpg 2 6 90 f 2014 0 7.37601999999999958 437 3 8 t f f f f f f f 322 | 2828 159087 I Say, Holmes! (Second Edition) http://cf.geekdo-images.com/images/pic2014564.jpg http://cf.geekdo-images.com/images/pic2014564_t.jpg 3 8 45 f 2014 0 5.85017000000000031 7893 0 -1 f f f t f f f f 323 | 2829 154203 Imperial Settlers http://cf.geekdo-images.com/images/pic2871265.jpg http://cf.geekdo-images.com/images/pic2871265_t.jpg 1 4 90 f 2014 0 7.65289000000000019 87 5 8 t f f f f f f f 324 | 2830 177739 Imperial Settlers: Atlanteans http://cf.geekdo-images.com/images/pic2520742.jpg http://cf.geekdo-images.com/images/pic2520742_t.jpg 1 5 90 t 2015 0 7.78486999999999973 -1 0 -1 t f f f f f f f 325 | 2831 169075 Imperial Settlers: Why Can't We Be Friends http://cf.geekdo-images.com/images/pic2305119.jpg http://cf.geekdo-images.com/images/pic2305119_t.jpg 1 4 90 t 2014 0 7.85667999999999989 -1 0 -1 t f f f f f f f 326 | 2832 156061 Imperialism: Road to Domination http://cf.geekdo-images.com/images/pic2288154.jpg http://cf.geekdo-images.com/images/pic2288154_t.jpg 2 4 90 f 2014 0 6.37073999999999963 4935 0 -1 f f f t f f f f 327 | 2833 144239 Impulse http://cf.geekdo-images.com/images/pic2008721.jpg http://cf.geekdo-images.com/images/pic2008721_t.jpg 2 6 60 f 2013 0 7.05076999999999998 911 0 -1 f f f t f f f f 328 | 2834 63888 Innovation http://cf.geekdo-images.com/images/pic2966859.png http://cf.geekdo-images.com/images/pic2966859_t.png 2 4 60 f 2010 0 7.26736999999999966 220 1 8 t f f f f f f f 329 | 2835 92898 Innovation: Echoes of the Past http://cf.geekdo-images.com/images/pic1159573.jpg http://cf.geekdo-images.com/images/pic1159573_t.jpg 2 5 60 t 2011 0 7.66826000000000008 -1 0 -1 t f f f f f f f 330 | 2836 174611 Ion: A Compound Building Game http://cf.geekdo-images.com/images/pic2605116.png http://cf.geekdo-images.com/images/pic2605116_t.png 2 7 40 f 2015 0 6.78659000000000034 5122 1 6 t f f f f f f f 331 | 2837 176494 Isle of Skye: From Chieftain to King http://cf.geekdo-images.com/images/pic2524838.jpg http://cf.geekdo-images.com/images/pic2524838_t.jpg 2 5 60 f 2015 0 7.53014000000000028 251 3 7 t f f f f f f f 332 | 2838 148949 Istanbul http://cf.geekdo-images.com/images/pic1885326.jpg http://cf.geekdo-images.com/images/pic1885326_t.jpg 2 5 60 f 2014 0 7.61978000000000044 94 3 7 f f f t f f f f 333 | 2839 54043 Jaipur http://cf.geekdo-images.com/images/pic725500.jpg http://cf.geekdo-images.com/images/pic725500_t.jpg 2 2 30 f 2009 0 7.54122999999999966 97 5 8 t f f f f f f f 334 | 2840 28023 Jamaica http://cf.geekdo-images.com/images/pic1502119.jpg http://cf.geekdo-images.com/images/pic1502119_t.jpg 2 6 60 f 2007 0 7.09971999999999959 371 3 7 t f f f f f f f 335 | 2841 122515 Keyflower http://cf.geekdo-images.com/images/pic2278942.jpg http://cf.geekdo-images.com/images/pic2278942_t.jpg 2 6 120 f 2012 0 7.98336999999999986 24 5 9 t f f f f f f f 336 | 2842 144058 Keyflower: The Farmers http://cf.geekdo-images.com/images/pic1743806.jpg http://cf.geekdo-images.com/images/pic1743806_t.jpg 2 6 120 t 2013 0 7.97370999999999963 -1 0 -1 t f f f f f f f 337 | 2843 163572 Keyflower: The Merchants http://cf.geekdo-images.com/images/pic2191035.jpg http://cf.geekdo-images.com/images/pic2191035_t.jpg 2 6 90 t 2014 0 8.2138100000000005 -1 0 -1 t f f f f f f f 338 | 2844 160499 King of New York http://cf.geekdo-images.com/images/pic2407103.jpg http://cf.geekdo-images.com/images/pic2407103_t.jpg 2 6 40 f 2014 0 7.31067999999999962 254 1 6 f f f t f f f f 339 | 2845 70323 King of Tokyo http://cf.geekdo-images.com/images/pic761434.jpg http://cf.geekdo-images.com/images/pic761434_t.jpg 2 6 30 f 2011 0 7.34888999999999992 150 6 7 t f f f f f f f 340 | 2846 147183 King of Tokyo: Halloween http://cf.geekdo-images.com/images/pic1867831.jpg http://cf.geekdo-images.com/images/pic1867831_t.jpg 2 6 30 t 2013 0 7.3211700000000004 -1 0 -1 t f f f f f f f 341 | 2847 127067 King of Tokyo: Power Up! http://cf.geekdo-images.com/images/pic1449032.jpg http://cf.geekdo-images.com/images/pic1449032_t.jpg 2 6 30 t 2012 0 7.70587999999999962 -1 0 -1 t f f f f f f f 342 | 2848 107529 Kingdom Builder http://cf.geekdo-images.com/images/pic1152359.jpg http://cf.geekdo-images.com/images/pic1152359_t.jpg 2 4 45 f 2011 0 7.04989000000000043 373 6 7 t f f f f f f f 343 | 2849 137396 Kingdom Builder: Crossroads http://cf.geekdo-images.com/images/pic1550248.jpg http://cf.geekdo-images.com/images/pic1550248_t.jpg 2 5 45 t 2013 0 7.70260999999999996 -1 0 -1 t f f f f f f f 344 | 2850 164205 King's Pouch http://cf.geekdo-images.com/images/pic2209254.jpg http://cf.geekdo-images.com/images/pic2209254_t.jpg 2 4 60 f 2014 0 6.82706999999999997 4005 1 6 f f f t f f f f 345 | 2851 27162 Kingsburg http://cf.geekdo-images.com/images/pic253057.jpg http://cf.geekdo-images.com/images/pic253057_t.jpg 2 5 90 f 2007 0 7.24948000000000015 211 2 7 t f f f f f f f 346 | 2852 39775 Kingsburg: To Forge a Realm http://cf.geekdo-images.com/images/pic1196154.jpg http://cf.geekdo-images.com/images/pic1196154_t.jpg 2 5 90 t 2009 0 7.91577000000000019 -1 0 -1 t f f f f f f f 347 | 2853 35677 Le Havre http://cf.geekdo-images.com/images/pic447994.jpg http://cf.geekdo-images.com/images/pic447994_t.jpg 1 5 200 f 2008 0 7.95931000000000033 21 1 7 t f f f f f f f 348 | 2854 129051 Le Havre: The Inland Port http://cf.geekdo-images.com/images/pic1451680.jpg http://cf.geekdo-images.com/images/pic1451680_t.jpg 2 2 30 f 2012 0 7.06125999999999987 642 2 7 t f f f f f f f 349 | 2855 146652 Legendary Encounters: An Alien Deck Building Game http://cf.geekdo-images.com/images/pic2225180.jpg http://cf.geekdo-images.com/images/pic2225180_t.jpg 1 5 45 f 2014 0 7.95434000000000019 58 3 6 t f f f f f f f 350 | 2856 127398 Legends of Andor http://cf.geekdo-images.com/images/pic2606106.jpg http://cf.geekdo-images.com/images/pic2606106_t.jpg 2 4 90 f 2012 0 7.35573000000000032 247 4 7 t f f f f f f f 351 | 2857 146963 Let Them Eat Shrimp! http://cf.geekdo-images.com/images/pic1906526.jpg http://cf.geekdo-images.com/images/pic1906526_t.jpg 2 5 20 f 2014 0 6.82754999999999956 4926 1 7 f f f t f f f f 352 | 2858 140620 Lewis & Clark http://cf.geekdo-images.com/images/pic1902092.jpg http://cf.geekdo-images.com/images/pic1902092_t.jpg 1 5 120 f 2013 0 7.65625 91 1 6 f f f t f f f f 353 | 2859 146886 La Granja http://cf.geekdo-images.com/images/pic2031777.jpg http://cf.geekdo-images.com/images/pic2031777_t.jpg 1 4 120 f 2014 0 7.9133899999999997 89 4 7 t f f f f f f f 354 | 2860 153213 Livestock Uprising http://cf.geekdo-images.com/images/pic2232226.jpg http://cf.geekdo-images.com/images/pic2232226_t.jpg 2 4 90 f 2014 0 6.94657999999999998 6834 0 -1 f f f t f f f f 355 | 2861 65781 London http://cf.geekdo-images.com/images/pic770317.jpg http://cf.geekdo-images.com/images/pic770317_t.jpg 2 4 90 f 2010 0 7.42164999999999964 196 2 7 t f f f f f f f 356 | 2862 823 Lord of the Rings http://cf.geekdo-images.com/images/pic479124.jpg http://cf.geekdo-images.com/images/pic479124_t.jpg 2 5 60 f 2001 0 6.80438999999999972 559 0 -1 f f f t f f f f 357 | 2863 130912 The Lord of the Rings Dice Building Game http://cf.geekdo-images.com/images/pic1624163.jpg http://cf.geekdo-images.com/images/pic1624163_t.jpg 2 4 30 f 2013 0 5.79201000000000032 5319 1 6 f f f t f f f f 358 | 2864 77423 The Lord of the Rings: The Card Game http://cf.geekdo-images.com/images/pic906495.jpg http://cf.geekdo-images.com/images/pic906495_t.jpg 1 4 60 f 2011 0 7.64470000000000027 83 39 9 t f f f f f f f 359 | 2865 129480 The Lord of the Rings: The Card Game – Heirs of Númenor http://cf.geekdo-images.com/images/pic1405987.jpg http://cf.geekdo-images.com/images/pic1405987_t.jpg 1 2 60 t 2012 0 7.88762000000000008 -1 0 -1 t f f f f f f f 360 | 2866 107933 The Lord of the Rings: The Card Game – Khazad-dûm http://cf.geekdo-images.com/images/pic1097391.jpg http://cf.geekdo-images.com/images/pic1097391_t.jpg 1 2 60 t 2011 0 7.94862000000000002 -1 0 -1 t f f f f f f f 361 | 2867 142875 The Lord of the Rings: The Card Game – The Black Riders http://cf.geekdo-images.com/images/pic1680705.jpg http://cf.geekdo-images.com/images/pic1680705_t.jpg 1 2 60 t 2013 0 8.32499999999999929 -1 0 -1 t f f f f f f f 362 | 2868 130715 The Lord of the Rings: The Card Game – The Hobbit: On the Doorstep http://cf.geekdo-images.com/images/pic1534818.jpg http://cf.geekdo-images.com/images/pic1534818_t.jpg 1 2 -1 t 2013 0 8.08367999999999931 -1 0 -1 t f f f f f f f 363 | 2869 123695 The Lord of the Rings: The Card Game – The Hobbit: Over Hill and Under Hill http://cf.geekdo-images.com/images/pic1324319.jpg http://cf.geekdo-images.com/images/pic1324319_t.jpg 1 2 60 t 2012 0 7.87894999999999968 -1 0 -1 t f f f f f f f 364 | 2870 164313 The Lord of the Rings: The Card Game – The Lost Realm http://cf.geekdo-images.com/images/pic2387795.jpg http://cf.geekdo-images.com/images/pic2387795_t.jpg 1 2 60 t 2015 0 8.51103999999999949 -1 0 -1 t f f f f f f f 365 | 2871 147723 The Lord of the Rings: The Card Game – The Road Darkens http://cf.geekdo-images.com/images/pic1940742.jpg http://cf.geekdo-images.com/images/pic1940742_t.jpg 1 2 60 t 2014 0 8.37856999999999985 -1 0 -1 t f f f f f f f 366 | 2872 164665 The Lord of the Rings: The Card Game – The Treason of Saruman http://cf.geekdo-images.com/images/pic2355165.jpg http://cf.geekdo-images.com/images/pic2355165_t.jpg 1 4 60 t 2015 0 8.47777999999999921 -1 0 -1 t f f f f f f f 367 | 2873 146574 The Lord of the Rings: The Card Game – The Voice of Isengard http://cf.geekdo-images.com/images/pic1854812.jpg http://cf.geekdo-images.com/images/pic1854812_t.jpg 1 2 60 t 2014 0 8.04643000000000086 -1 0 -1 t f f f f f f f 368 | 2874 18833 Lord of the Rings: The Confrontation http://cf.geekdo-images.com/images/pic1680704.jpg http://cf.geekdo-images.com/images/pic1680704_t.jpg 2 2 30 f 2013 0 7.53031000000000006 184 0 -1 f f t f f f f f 369 | 2875 152510 The Lord of the Rings: The Return of the King Deck-Building Game http://cf.geekdo-images.com/images/pic2454386.jpg http://cf.geekdo-images.com/images/pic2454386_t.jpg 2 5 45 f 2014 0 7.3251299999999997 2860 1 7 f f f t f f f f 370 | 2876 110327 Lords of Waterdeep http://cf.geekdo-images.com/images/pic1116080.jpg http://cf.geekdo-images.com/images/pic1116080_t.jpg 2 5 120 f 2012 0 7.80067999999999984 36 10 8 t f f f f f f f 371 | 2877 134342 Lords of Waterdeep: Scoundrels of Skullport http://cf.geekdo-images.com/images/pic1590236.jpg http://cf.geekdo-images.com/images/pic1590236_t.jpg 2 6 60 t 2013 0 8.23597000000000001 -1 0 -1 t f f f f f f f 372 | 2878 50 Lost Cities http://cf.geekdo-images.com/images/pic2606107.jpg http://cf.geekdo-images.com/images/pic2606107_t.jpg 2 2 30 f 1999 0 7.12575000000000003 278 5 8 t f f f f f f f 373 | 2879 42487 Lost Cities: The Board Game http://cf.geekdo-images.com/images/pic2606157.jpg http://cf.geekdo-images.com/images/pic2606157_t.jpg 2 4 60 f 2008 0 6.84680999999999962 787 1 6 f f f t f f f f 374 | 2880 158340 Lost Legacy: Flying Garden http://cf.geekdo-images.com/images/pic1998163.jpg http://cf.geekdo-images.com/images/pic1998163_t.jpg 2 4 10 f 2014 0 6.90826999999999991 1165 2 7 t f f f f f f f 375 | 2881 158339 Lost Legacy: The Starship http://cf.geekdo-images.com/images/pic1998162.jpg http://cf.geekdo-images.com/images/pic1998162_t.jpg 2 4 10 f 2014 0 6.80616000000000021 953 2 7 t f f f f f f f 376 | 2882 129622 Love Letter http://cf.geekdo-images.com/images/pic1401448.jpg http://cf.geekdo-images.com/images/pic1401448_t.jpg 2 4 20 f 2012 0 7.35874000000000006 140 6 7 t f f f f f f f 377 | 2883 169611 Love Letter: The Hobbit – The Battle of the Five Armies http://cf.geekdo-images.com/images/pic2293327.jpg http://cf.geekdo-images.com/images/pic2293327_t.jpg 2 4 30 f 2015 0 7.27073999999999998 1150 0 -1 t f f f f f f f 378 | 2884 143884 Machi Koro http://cf.geekdo-images.com/images/pic1992476.jpg http://cf.geekdo-images.com/images/pic1992476_t.jpg 2 4 30 f 2012 0 6.84185999999999961 524 6 6 f f f t f f f f 379 | 2885 143789 Machi Koro: Harbor Expansion http://cf.geekdo-images.com/images/pic2034049.jpg http://cf.geekdo-images.com/images/pic2034049_t.jpg 2 5 40 t 2013 0 7.26015000000000033 -1 0 -1 t f f f f f f f 380 | 2886 150930 Mad City http://cf.geekdo-images.com/images/pic1867064.jpg http://cf.geekdo-images.com/images/pic1867064_t.jpg 1 6 30 f 2014 0 6.31993999999999989 4305 2 6 t f f f f f f f 381 | 2887 178140 Maelstrom: An Expansion for Captain's Wager http://cf.geekdo-images.com/images/pic2530969.jpg http://cf.geekdo-images.com/images/pic2530969_t.jpg 2 5 40 t 2015 0 6.68182000000000009 -1 0 -1 t f f f f f f f 382 | 2888 96848 Mage Knight Board Game http://cf.geekdo-images.com/images/pic1083380.jpg http://cf.geekdo-images.com/images/pic1083380_t.jpg 1 4 150 f 2011 0 8.14658999999999978 10 3 8 t f f f f f f f 383 | 2889 144763 Mage Knight Board Game: Krang Character Expansion http://cf.geekdo-images.com/images/pic1883789.jpg http://cf.geekdo-images.com/images/pic1883789_t.jpg 1 5 90 t 2013 0 8.10543000000000013 -1 0 -1 t f f f f f f f 384 | 2890 130704 Mage Knight Board Game: The Lost Legion http://cf.geekdo-images.com/images/pic1421682.jpg http://cf.geekdo-images.com/images/pic1421682_t.jpg 1 5 240 t 2012 0 8.83239999999999981 -1 0 -1 t f f f f f f f 385 | 2891 95613 Mammut http://cf.geekdo-images.com/images/pic1154690.jpg http://cf.geekdo-images.com/images/pic1154690_t.jpg 2 5 30 f 2011 0 6.41976000000000013 2583 1 6 f f f t f f f f 386 | 2892 63628 The Manhattan Project http://cf.geekdo-images.com/images/pic1222522.jpg http://cf.geekdo-images.com/images/pic1222522_t.jpg 2 5 120 f 2012 0 7.52031000000000027 155 1 6 f f f t f f f f 387 | 2893 108421 The Manhattan Project: Nations Expansion http://cf.geekdo-images.com/images/pic1092706.jpg http://cf.geekdo-images.com/images/pic1092706_t.jpg 2 5 120 t 2012 0 7.54769000000000023 -1 0 -1 f f f t f f f f 388 | 2894 99875 Martian Dice http://cf.geekdo-images.com/images/pic1516575.jpg http://cf.geekdo-images.com/images/pic1516575_t.jpg 2 99 10 f 2011 0 6.38496000000000041 1483 2 6 t f f f f f f f 389 | 2895 1261 Medina http://cf.geekdo-images.com/images/pic580033.jpg http://cf.geekdo-images.com/images/pic580033_t.jpg 3 4 60 f 2001 0 7.08792000000000044 553 0 -1 t f f f f f f f 390 | 2896 10630 Memoir '44 http://cf.geekdo-images.com/images/pic43663.jpg http://cf.geekdo-images.com/images/pic43663_t.jpg 2 2 60 f 2004 0 7.52271999999999963 103 1 7 t f f f f f f f 391 | 2897 2955 Mexica http://cf.geekdo-images.com/images/pic2470537.jpg http://cf.geekdo-images.com/images/pic2470537_t.jpg 2 4 90 f 2002 0 7.1154200000000003 509 1 7 t f f f f f f f 392 | 2898 124708 Mice and Mystics http://cf.geekdo-images.com/images/pic1312072.jpg http://cf.geekdo-images.com/images/pic1312072_t.jpg 1 4 120 f 2012 0 7.53486999999999973 131 6 7 t f f f f f f f 393 | 2899 117914 Milestones http://cf.geekdo-images.com/images/pic1336176.jpg http://cf.geekdo-images.com/images/pic1336176_t.jpg 2 4 60 f 2012 0 6.60670000000000002 1699 0 -1 f f t f f f f f 394 | 2900 176920 Mission: Red Planet (second edition) http://cf.geekdo-images.com/images/pic2499748.jpg http://cf.geekdo-images.com/images/pic2499748_t.jpg 2 6 90 f 2015 0 7.80511999999999961 249 1 7 t f f f f f f f 395 | 2901 156546 Monikers http://cf.geekdo-images.com/images/pic2357036.png http://cf.geekdo-images.com/images/pic2357036_t.png 4 20 60 f 2015 0 7.89353000000000016 1113 1 7 t f f f f f f f 396 | 2902 122298 Morels http://cf.geekdo-images.com/images/pic1284667.jpg http://cf.geekdo-images.com/images/pic1284667_t.jpg 2 2 30 f 2012 0 7.12994000000000039 450 1 8 f f f t f f f f 397 | 2903 175199 Mottainai http://cf.geekdo-images.com/images/pic2688214.jpg http://cf.geekdo-images.com/images/pic2688214_t.jpg 2 5 30 f 2015 0 7.15353999999999957 759 1 7 t f f f f f f f 398 | 2904 21763 Mr. Jack http://cf.geekdo-images.com/images/pic156046.jpg http://cf.geekdo-images.com/images/pic156046_t.jpg 2 2 30 f 2006 0 7.09916999999999998 325 0 -1 f f f t f f f f 399 | 2905 30362 Mr. Jack Extension http://cf.geekdo-images.com/images/pic263545.jpg http://cf.geekdo-images.com/images/pic263545_t.jpg 2 2 30 t 2007 0 7.27064999999999984 -1 0 -1 f f f t f f f f 400 | 2906 172381 My Village http://cf.geekdo-images.com/images/pic2760567.jpg http://cf.geekdo-images.com/images/pic2760567_t.jpg 2 4 90 f 2015 0 7.24357000000000006 1125 1 7 t f f f f f f f 401 | 2907 133632 Mythotopia http://cf.geekdo-images.com/images/pic2257592.jpg http://cf.geekdo-images.com/images/pic2257592_t.jpg 2 4 120 f 2014 0 7.04832000000000036 1013 3 7 t f f f f f f f 402 | 2908 126042 Nations http://cf.geekdo-images.com/images/pic1591406.png http://cf.geekdo-images.com/images/pic1591406_t.png 1 5 200 f 2013 0 7.80442999999999998 61 2 8 f f f t f f f f 403 | 2909 157809 Nations: The Dice Game http://cf.geekdo-images.com/images/pic1988890.jpg http://cf.geekdo-images.com/images/pic1988890_t.jpg 1 4 40 f 2014 0 6.98543000000000003 665 1 7 t f f f f f f f 404 | 2910 96007 The New Era http://cf.geekdo-images.com/images/pic1106275.jpg http://cf.geekdo-images.com/images/pic1106275_t.jpg 2 5 60 f 2011 0 7.69310000000000027 487 1 6 f f f t f f f f 405 | 2911 174660 New York 1901 http://cf.geekdo-images.com/images/pic2515532.jpg http://cf.geekdo-images.com/images/pic2515532_t.jpg 2 4 45 f 2015 0 7.07237000000000027 677 3 8 t f f f f f f f 406 | 2912 12942 No Thanks! http://cf.geekdo-images.com/images/pic2602161.jpg http://cf.geekdo-images.com/images/pic2602161_t.jpg 3 5 20 f 2004 0 7.03938000000000041 353 1 -1 t f f f f f f f 407 | 2913 176361 One Night Revolution http://cf.geekdo-images.com/images/pic2513302.jpg http://cf.geekdo-images.com/images/pic2513302_t.jpg 3 10 15 f 2015 0 6.46926000000000023 2205 5 7 t f f f f f f f 408 | 2914 147949 One Night Ultimate Werewolf http://cf.geekdo-images.com/images/pic1809823.jpg http://cf.geekdo-images.com/images/pic1809823_t.jpg 3 10 10 f 2014 0 7.44632000000000005 159 0 -1 f f f t f f f f 409 | 2915 163166 One Night Ultimate Werewolf - Daybreak http://cf.geekdo-images.com/images/pic2225958.jpg http://cf.geekdo-images.com/images/pic2225958_t.jpg 3 7 10 f 2015 0 7.68695000000000039 324 0 -1 f f f t f f f f 410 | 2916 156336 Onirim http://cf.geekdo-images.com/images/pic2257657.png http://cf.geekdo-images.com/images/pic2257657_t.png 1 2 15 f 2014 0 7.2913199999999998 615 4 7 t f f f f f f f 411 | 2917 164928 Orléans http://cf.geekdo-images.com/images/pic2578828.png http://cf.geekdo-images.com/images/pic2578828_t.png 2 4 90 f 2014 0 8.03289999999999971 45 3 8 t f f f f f f f 412 | 2918 183682 Orléans: Invasion http://cf.geekdo-images.com/images/pic2660026.jpg http://cf.geekdo-images.com/images/pic2660026_t.jpg 1 5 120 t 2015 0 8.40121000000000073 -1 0 -1 t f f f f f f f 413 | 2919 23953 Outside the Scope of BGG http://cf.geekdo-images.com/images/pic193671.jpg http://cf.geekdo-images.com/images/pic193671_t.jpg -1 -1 -1 f -1 0 6.75860000000000039 1907 0 -1 f f t f f f f f 414 | 2920 30549 Pandemic http://cf.geekdo-images.com/images/pic1534148.jpg http://cf.geekdo-images.com/images/pic1534148_t.jpg 2 4 90 f 2008 0 7.66598999999999986 51 15 8 t f f f f f f f 415 | 2921 161936 Pandemic Legacy: Season 1 http://cf.geekdo-images.com/images/pic2452831.png http://cf.geekdo-images.com/images/pic2452831_t.png 2 4 60 f 2015 0 8.63846000000000025 1 22 10 t f f f f f f f 416 | 2922 157789 Pandemic: Contagion http://cf.geekdo-images.com/images/pic2037506.png http://cf.geekdo-images.com/images/pic2037506_t.png 2 5 30 f 2014 0 6.56228000000000034 1382 1 7 f f f t f f f f 417 | 2923 137136 Pandemic: In the Lab http://cf.geekdo-images.com/images/pic1632485.jpg http://cf.geekdo-images.com/images/pic1632485_t.jpg 1 6 60 t 2013 0 8.01983000000000068 -1 0 -1 t f f f f f f f 418 | 2924 40849 Pandemic: On the Brink http://cf.geekdo-images.com/images/pic1546621.jpg http://cf.geekdo-images.com/images/pic1546621_t.jpg 2 5 45 t 2009 0 8.11899000000000015 -1 0 -1 t f f f f f f f 419 | 2925 168703 Pandemic: State of Emergency http://cf.geekdo-images.com/images/pic2297198.png http://cf.geekdo-images.com/images/pic2297198_t.png 2 4 45 t 2015 0 7.73045000000000027 -1 0 -1 t f f f f f f f 420 | 2926 150658 Pandemic: The Cure http://cf.geekdo-images.com/images/pic2225998.jpg http://cf.geekdo-images.com/images/pic2225998_t.jpg 2 5 30 f 2014 0 7.45157000000000025 219 3 7 t f f f f f f f 421 | 2927 141572 Paperback http://cf.geekdo-images.com/images/pic1991455.jpg http://cf.geekdo-images.com/images/pic1991455_t.jpg 2 5 45 f 2014 0 7.44188999999999989 301 1 6 f f f t f f f f 422 | 2928 163412 Patchwork http://cf.geekdo-images.com/images/pic2270442.jpg http://cf.geekdo-images.com/images/pic2270442_t.jpg 2 2 30 f 2014 0 7.88459000000000021 41 2 7 t f f f f f f f 423 | 2929 133038 Pathfinder Adventure Card Game: Rise of the Runelords – Base Set http://cf.geekdo-images.com/images/pic1775517.jpg http://cf.geekdo-images.com/images/pic1775517_t.jpg 1 4 90 f 2013 0 7.44803999999999977 163 13 7 t f f f f f f f 424 | 2930 139037 Pathfinder Adventure Card Game: Rise of the Runelords – Character Add-On Deck http://cf.geekdo-images.com/images/pic1775530.jpg http://cf.geekdo-images.com/images/pic1775530_t.jpg 1 6 90 t 2013 0 7.74242000000000008 -1 0 -1 t f f f f f f f 425 | 2931 149261 Pathfinder Adventure Card Game: Rise of the Runelords – Fortress of the Stone Giants Adventure Deck 4 http://cf.geekdo-images.com/images/pic1968268.jpg http://cf.geekdo-images.com/images/pic1968268_t.jpg 1 6 -1 t 2014 0 7.78744999999999976 -1 0 -1 t f f f f f f f 426 | 2932 150074 Pathfinder Adventure Card Game: Rise of the Runelords – Sins of the Saviors Adventure Deck 5 http://cf.geekdo-images.com/images/pic2024979.jpg http://cf.geekdo-images.com/images/pic2024979_t.jpg 1 6 90 t 2014 0 7.78549999999999986 -1 0 -1 t f f f f f f f 427 | 2933 150798 Pathfinder Adventure Card Game: Rise of the Runelords – Spires of Xin-Shalast Adventure Deck 6 http://cf.geekdo-images.com/images/pic2081109.jpg http://cf.geekdo-images.com/images/pic2081109_t.jpg 1 6 120 t 2014 0 7.84478000000000009 -1 0 -1 t f f f f f f f 428 | 2934 144873 Pathfinder Adventure Card Game: Rise of the Runelords – The Hook Mountain Massacre Adventure Deck 3 http://cf.geekdo-images.com/images/pic1887335.jpg http://cf.geekdo-images.com/images/pic1887335_t.jpg 1 6 90 t 2013 0 7.80698000000000025 -1 0 -1 t f f f f f f f 429 | 2935 142423 Pathfinder Adventure Card Game: Rise of the Runelords – The Skinsaw Murders Adventure Deck 2 http://cf.geekdo-images.com/images/pic1829650.jpg http://cf.geekdo-images.com/images/pic1829650_t.jpg 1 6 90 t 2013 0 7.74366000000000021 -1 0 -1 t f f f f f f f 430 | 2936 157992 Pathfinder Adventure Card Game: Skull & Shackles – "Ranzak" Promo Character Card Set http://cf.geekdo-images.com/images/pic1993904.jpg http://cf.geekdo-images.com/images/pic1993904_t.jpg 1 4 90 t 2014 0 7.94237000000000037 -1 0 -1 f f f t f f f f 431 | 2937 151007 Pathfinder Adventure Card Game: Skull & Shackles – Base Set http://cf.geekdo-images.com/images/pic2268287.jpg http://cf.geekdo-images.com/images/pic2268287_t.jpg 1 4 120 f 2014 0 7.66021999999999981 490 8 7 f f f t f f f f 432 | 2938 151008 Pathfinder Adventure Card Game: Skull & Shackles – Character Add-On Deck http://cf.geekdo-images.com/images/pic2268289.jpg http://cf.geekdo-images.com/images/pic2268289_t.jpg 1 6 120 t 2014 0 8.06709000000000032 -1 0 -1 f f f t f f f f 433 | 2939 157991 Pathfinder Adventure Card Game: Skull & Shackles Adventure Deck 2 – Raiders of the Fever Sea http://cf.geekdo-images.com/images/pic2268290.jpg http://cf.geekdo-images.com/images/pic2268290_t.jpg 1 6 90 t 2014 0 7.94296999999999986 -1 0 -1 f f f t f f f f 434 | 2940 159038 Pathfinder Adventure Card Game: Skull & Shackles Adventure Deck 3 – Tempest Rising http://cf.geekdo-images.com/images/pic2352745.jpg http://cf.geekdo-images.com/images/pic2352745_t.jpg 1 6 90 t 2014 0 7.95624999999999982 -1 0 -1 f f f t f f f f 435 | 2941 160589 Pathfinder Adventure Card Game: Skull & Shackles Adventure Deck 4 – Island of Empty Eyes http://cf.geekdo-images.com/images/pic2352729.jpg http://cf.geekdo-images.com/images/pic2352729_t.jpg 1 6 90 t 2014 0 7.93858999999999959 -1 0 -1 f f f t f f f f 436 | 2942 169416 Pathfinder Adventure Card Game: Wrath of the Righteous – Base Set http://cf.geekdo-images.com/images/pic2564484.jpg http://cf.geekdo-images.com/images/pic2564484_t.jpg 1 4 120 f 2015 0 7.89515999999999973 1328 3 7 t f f f f f f f 437 | 2943 170960 Pathfinder Adventure Card Game: Wrath of the Righteous – Character Add-On Deck http://cf.geekdo-images.com/images/pic2551476.jpg http://cf.geekdo-images.com/images/pic2551476_t.jpg 1 4 -1 t 2015 0 8.1940299999999997 -1 0 -1 t f f f f f f f 438 | 2944 166298 Peptide: A Protein Building Game http://cf.geekdo-images.com/images/pic2614392.png http://cf.geekdo-images.com/images/pic2614392_t.png 2 6 40 f 2014 0 7.04687999999999981 6537 0 -1 f t f f f f f f 439 | 2945 2651 Power Grid http://cf.geekdo-images.com/images/pic173153.jpg http://cf.geekdo-images.com/images/pic173153_t.jpg 2 6 120 f 2004 0 7.97355999999999998 16 0 -1 f f f t f f f f 440 | 2946 155873 Power Grid http://cf.geekdo-images.com/images/pic2056160.jpg http://cf.geekdo-images.com/images/pic2056160_t.jpg 2 6 120 f 2014 0 8.21293999999999969 179 1 7 t f f f f f f f 441 | 2947 19319 Power Grid: France/Italy http://cf.geekdo-images.com/images/pic1553323.jpg http://cf.geekdo-images.com/images/pic1553323_t.jpg 2 6 120 t 2005 0 7.96501999999999999 -1 0 -1 f f f t f f f f 442 | 2948 108667 Power Grid: The Robots http://cf.geekdo-images.com/images/pic1103946.jpg http://cf.geekdo-images.com/images/pic1103946_t.jpg 2 5 120 t 2011 0 7.19177 -1 0 -1 t f f f f f f f 443 | 2949 159375 Prime Climb http://cf.geekdo-images.com/images/pic2326262.png http://cf.geekdo-images.com/images/pic2326262_t.png 2 4 45 f 2014 0 6.87143000000000015 7474 0 -1 f f f t f f f f 444 | 2950 8095 Prophecy http://cf.geekdo-images.com/images/pic1919190.jpg http://cf.geekdo-images.com/images/pic1919190_t.jpg 2 5 180 f 2014 0 6.71028999999999964 1095 0 -1 f f f t f f f f 445 | 2951 145203 Prosperity http://cf.geekdo-images.com/images/pic1717367.jpg http://cf.geekdo-images.com/images/pic1717367_t.jpg 2 4 60 f 2013 0 6.81815999999999978 1273 1 6 f f f t f f f f 446 | 2952 3076 Puerto Rico http://cf.geekdo-images.com/images/pic158548.jpg http://cf.geekdo-images.com/images/pic158548_t.jpg 2 5 150 f 2002 0 8.12214999999999954 6 1 7 t f f f f f f f 447 | 2953 12382 Puerto Rico: Expansion I – New Buildings http://cf.geekdo-images.com/images/pic182535.jpg http://cf.geekdo-images.com/images/pic182535_t.jpg 2 5 90 t 2004 0 7.54947999999999997 -1 0 -1 t f f f f f f f 448 | 2954 176396 Quadropolis http://cf.geekdo-images.com/images/pic2840020.jpg http://cf.geekdo-images.com/images/pic2840020_t.jpg 2 4 60 f 2016 0 7.64308000000000032 431 2 8 t f f f f f f f 449 | 2955 172547 Queen's Architect http://cf.geekdo-images.com/images/pic2448516.jpg http://cf.geekdo-images.com/images/pic2448516_t.jpg 2 4 60 f 2015 0 7.06198999999999977 1542 1 7 t f f f f f f f 450 | 2956 25669 Qwirkle http://cf.geekdo-images.com/images/pic309353.jpg http://cf.geekdo-images.com/images/pic309353_t.jpg 2 4 45 f 2006 0 6.83584000000000014 551 4 8 t f f f f f f f 451 | 2957 35503 Ra: The Dice Game http://cf.geekdo-images.com/images/pic533845.jpg http://cf.geekdo-images.com/images/pic533845_t.jpg 2 4 45 f 2009 0 6.75129000000000001 909 1 6 f f f t f f f f 452 | 2958 28143 Race for the Galaxy http://cf.geekdo-images.com/images/pic236327.jpg http://cf.geekdo-images.com/images/pic236327_t.jpg 2 4 60 f 2007 0 7.78481999999999985 32 24 8 t f f f f f f f 453 | 2959 92932 Race for the Galaxy: Alien Artifacts http://cf.geekdo-images.com/images/pic1839311.jpg http://cf.geekdo-images.com/images/pic1839311_t.jpg 2 5 45 t 2013 0 7.5602999999999998 -1 0 -1 t f f f f f f f 454 | 2960 40210 Race for the Galaxy: Rebel vs Imperium http://cf.geekdo-images.com/images/pic445501.jpg http://cf.geekdo-images.com/images/pic445501_t.jpg 1 6 60 t 2009 0 7.96448999999999963 -1 0 -1 t f f f f f f f 455 | 2961 34499 Race for the Galaxy: The Gathering Storm http://cf.geekdo-images.com/images/pic376993.jpg http://cf.geekdo-images.com/images/pic376993_t.jpg 1 5 60 t 2008 0 8.08514000000000088 -1 0 -1 t f f f f f f f 456 | 2962 161599 Race for the Galaxy: Xeno Invasion http://cf.geekdo-images.com/images/pic2682884.jpg http://cf.geekdo-images.com/images/pic2682884_t.jpg 2 5 -1 t 2015 0 8.08070999999999984 -1 0 8 t f f f f f f f 457 | 2963 30450 Railways of Europe http://cf.geekdo-images.com/images/pic787500.jpg http://cf.geekdo-images.com/images/pic787500_t.jpg 2 5 120 t 2008 0 8.10717999999999961 -1 0 -1 t f f f f f f f 458 | 2964 17133 Railways of the World http://cf.geekdo-images.com/images/pic445850.jpg http://cf.geekdo-images.com/images/pic445850_t.jpg 2 6 120 f 2005 0 7.70866000000000007 70 1 7 f f f t f f f f 459 | 2965 99692 Railways of the World: Event Deck http://cf.geekdo-images.com/images/pic2845787.jpg http://cf.geekdo-images.com/images/pic2845787_t.jpg 2 6 120 t 2011 0 7.40334999999999965 -1 0 -1 t f f f f f f f 460 | 2966 41114 The Resistance http://cf.geekdo-images.com/images/pic2576459.jpg http://cf.geekdo-images.com/images/pic2576459_t.jpg 5 10 30 f 2009 0 7.4751000000000003 108 7 8 t f f f f f f f 461 | 2967 128882 The Resistance: Avalon http://cf.geekdo-images.com/images/pic1398895.jpg http://cf.geekdo-images.com/images/pic1398895_t.jpg 5 10 30 f 2012 0 7.78488000000000024 47 0 -1 f f f t f f f f 462 | 2968 163322 The Resistance: Hidden Agenda & Hostile Intent http://cf.geekdo-images.com/images/pic2243281.png http://cf.geekdo-images.com/images/pic2243281_t.png 5 10 30 t 2014 0 7.75386000000000042 -1 0 -1 t f f f f f f f 463 | 2969 66056 The Rivals for Catan http://cf.geekdo-images.com/images/pic850850.jpg http://cf.geekdo-images.com/images/pic850850_t.jpg 2 2 45 f 2010 0 7.11340000000000039 503 10 8 t f f f f f f f 464 | 2970 93401 The Rivals for Catan: Age of Darkness http://cf.geekdo-images.com/images/pic1101867.jpg http://cf.geekdo-images.com/images/pic1101867_t.jpg 2 2 75 t 2011 0 7.6532 -1 0 -1 t f f f f f f f 465 | 2971 127437 The Rivals for Catan: Age of Enlightenment http://cf.geekdo-images.com/images/pic1447247.jpg http://cf.geekdo-images.com/images/pic1447247_t.jpg 2 2 75 t 2012 0 7.77195999999999998 -1 0 -1 t f f f f f f f 466 | 2972 121921 Robinson Crusoe: Adventures on the Cursed Island http://cf.geekdo-images.com/images/pic1413154.jpg http://cf.geekdo-images.com/images/pic1413154_t.jpg 1 4 180 f 2012 0 8.0475999999999992 18 2 7 t f f f f f f f 467 | 2973 144722 Robinson Crusoe: Adventures on the Cursed Island – Voyage of the Beagle (Vol. 1) http://cf.geekdo-images.com/images/pic1997048.jpg http://cf.geekdo-images.com/images/pic1997048_t.jpg 1 4 120 t 2013 0 8.51154999999999973 -1 0 -1 t f f f f f f f 468 | 2974 129090 Roll For It! http://cf.geekdo-images.com/images/pic1638562.png http://cf.geekdo-images.com/images/pic1638562_t.png 2 4 30 f 2011 0 6.22585999999999995 2368 2 6 t f f f f f f f 469 | 2975 132531 Roll for the Galaxy http://cf.geekdo-images.com/images/pic1473629.jpg http://cf.geekdo-images.com/images/pic1473629_t.jpg 2 5 45 f 2014 0 7.90653000000000006 35 8 8 t f f f f f f f 470 | 2976 175754 Roll for the Galaxy: Ambition http://cf.geekdo-images.com/images/pic2700805.jpg http://cf.geekdo-images.com/images/pic2700805_t.jpg 2 5 60 t 2015 0 8.25360000000000049 -1 0 -1 t f f f f f f f 471 | 2977 37380 Roll Through the Ages: The Bronze Age http://cf.geekdo-images.com/images/pic986758.jpg http://cf.geekdo-images.com/images/pic986758_t.jpg 1 4 45 f 2008 0 6.93137000000000025 443 2 7 f f f t f f f f 472 | 2978 150926 Roll Through the Ages: The Iron Age http://cf.geekdo-images.com/images/pic1840147.jpg http://cf.geekdo-images.com/images/pic1840147_t.jpg 1 4 60 f 2014 0 6.79847000000000001 1730 2 7 t f f f f f f f 473 | 2979 143701 Romance of the Nine Empires http://cf.geekdo-images.com/images/pic1683905.jpg http://cf.geekdo-images.com/images/pic1683905_t.jpg 2 5 45 f 2013 0 6.42154999999999987 5829 0 -1 f f f t f f f f 474 | 2980 89910 Run, Fight, or Die! http://cf.geekdo-images.com/images/pic2039968.jpg http://cf.geekdo-images.com/images/pic2039968_t.jpg 1 4 40 f 2014 0 7.11036000000000001 991 1 6 f f f t f f f f 475 | 2981 144733 Russian Railroads http://cf.geekdo-images.com/images/pic1772936.jpg http://cf.geekdo-images.com/images/pic1772936_t.jpg 2 4 120 f 2013 0 7.84565999999999963 50 1 7 t f f f f f f f 476 | 2982 9220 Saboteur http://cf.geekdo-images.com/images/pic2602139.jpg http://cf.geekdo-images.com/images/pic2602139_t.jpg 3 10 30 f 2004 0 6.66002000000000027 710 1 6 t f f f f f f f 477 | 2983 91072 Saboteur 2 http://cf.geekdo-images.com/images/pic2602154.jpg http://cf.geekdo-images.com/images/pic2602154_t.jpg 2 12 45 t 2011 0 7.05642999999999976 -1 0 -1 t f f f f f f f 478 | 2984 141736 Sail to India http://cf.geekdo-images.com/images/pic1756958.jpg http://cf.geekdo-images.com/images/pic1756958_t.jpg 3 4 60 f 2013 0 6.86043000000000003 899 2 6 t f f f f f f f 479 | 2985 3 Samurai http://cf.geekdo-images.com/images/pic2571921.png http://cf.geekdo-images.com/images/pic2571921_t.png 2 4 60 f 2015 0 7.43871000000000038 135 1 7 t f f f f f f f 480 | 2986 145659 Scoville http://cf.geekdo-images.com/images/pic1903464.jpg http://cf.geekdo-images.com/images/pic1903464_t.jpg 2 6 90 f 2014 0 7.25863999999999976 497 1 7 t f f f f f f f 481 | 2987 24068 Shadow Hunters http://cf.geekdo-images.com/images/pic1215982.jpg http://cf.geekdo-images.com/images/pic1215982_t.jpg 4 8 45 f 2005 0 6.86923999999999957 564 4 6 t f f f f f f f 482 | 2988 112092 Shadowrift http://cf.geekdo-images.com/images/pic2570089.jpg http://cf.geekdo-images.com/images/pic2570089_t.jpg 1 6 120 f 2012 0 7.14161000000000001 1422 0 -1 t f f f f f f f 483 | 2989 153435 Shadowrift: Archfiends http://cf.geekdo-images.com/images/pic1903394.jpg http://cf.geekdo-images.com/images/pic1903394_t.jpg 2 6 45 t 2015 0 7.69167000000000023 -1 0 -1 t f f f f f f f 484 | 2990 135382 Shadowrun: Crossfire http://cf.geekdo-images.com/images/pic2060466.jpg http://cf.geekdo-images.com/images/pic2060466_t.jpg 1 4 60 f 2014 0 7.33229000000000042 506 1 6 f f f t f f f f 485 | 2991 157969 Sheriff of Nottingham http://cf.geekdo-images.com/images/pic2075830.jpg http://cf.geekdo-images.com/images/pic2075830_t.jpg 3 5 60 f 2014 0 7.45854000000000017 138 3 8 t f f f f f f f 486 | 2992 2511 Sherlock Holmes Consulting Detective http://cf.geekdo-images.com/images/pic1977064.jpg http://cf.geekdo-images.com/images/pic1977064_t.jpg 1 8 120 f 1981 0 7.83908000000000005 56 0 -1 t f f f f f f f 487 | 2993 153737 Ships http://cf.geekdo-images.com/images/pic2721731.jpg http://cf.geekdo-images.com/images/pic2721731_t.jpg 2 4 120 f 2015 0 7.09647999999999968 1356 0 -1 f t f f f f f f 488 | 2994 177678 Signorie http://cf.geekdo-images.com/images/pic2684003.jpg http://cf.geekdo-images.com/images/pic2684003_t.jpg 2 4 120 f 2015 0 7.64909000000000017 716 1 7 t f f f f f f f 489 | 2995 8217 San Juan http://cf.geekdo-images.com/images/pic174174.jpg http://cf.geekdo-images.com/images/pic174174_t.jpg 2 4 60 f 2004 0 7.30670000000000019 174 3 8 t f f f f f f f 490 | 2996 122522 Smash Up http://cf.geekdo-images.com/images/pic1269874.jpg http://cf.geekdo-images.com/images/pic1269874_t.jpg 2 4 45 f 2012 0 6.94259000000000004 463 1 6 t f f f f f f f 491 | 2997 160018 Smash Up: Monster Smash http://cf.geekdo-images.com/images/pic2038798.jpg http://cf.geekdo-images.com/images/pic2038798_t.jpg 2 4 45 f 2014 0 7.59562999999999988 459 0 -1 t f f f f f f f 492 | 2998 151004 Smash Up: Science Fiction Double Feature http://cf.geekdo-images.com/images/pic1857661.jpg http://cf.geekdo-images.com/images/pic1857661_t.jpg 2 4 45 f 2014 0 7.51970999999999989 376 0 -1 t f f f f f f f 493 | 2999 157427 Smash Up: The Big Geeky Box http://cf.geekdo-images.com/images/pic1987355.jpg http://cf.geekdo-images.com/images/pic1987355_t.jpg 2 4 -1 t 2014 0 7.4706999999999999 -1 0 -1 t f f f f f f f 494 | 3000 38054 Snow Tails http://cf.geekdo-images.com/images/pic2574022.png http://cf.geekdo-images.com/images/pic2574022_t.png 2 5 60 f 2008 0 6.99138000000000037 481 1 6 t f f f f f f f 495 | 3001 38453 Space Alert http://cf.geekdo-images.com/images/pic384313.jpg http://cf.geekdo-images.com/images/pic384313_t.jpg 1 5 30 f 2008 0 7.56557999999999975 100 1 6 f f f t f f f f 496 | 3002 165838 Space Hulk (fourth edition) http://cf.geekdo-images.com/images/pic2245229.jpg http://cf.geekdo-images.com/images/pic2245229_t.jpg 2 2 60 f 2014 0 8.08700999999999937 590 0 -1 f t f f f f f f 497 | 3003 71721 Space Hulk: Death Angel – The Card Game http://cf.geekdo-images.com/images/pic1873572.jpg http://cf.geekdo-images.com/images/pic1873572_t.jpg 1 6 30 f 2010 0 6.96989999999999998 452 1 7 t f f f f f f f 498 | 3004 94964 Space Hulk: Death Angel – The Card Game – Mission Pack 1 http://cf.geekdo-images.com/images/pic1431153.jpg http://cf.geekdo-images.com/images/pic1431153_t.jpg 1 6 30 t 2011 0 7.33122000000000007 -1 0 -1 t f f f f f f f 499 | 3005 94963 Space Hulk: Death Angel – The Card Game: Space Marine Pack 1 http://cf.geekdo-images.com/images/pic1431151.jpg http://cf.geekdo-images.com/images/pic1431151_t.jpg 1 6 30 t 2011 0 7.34646000000000043 -1 0 -1 t f f f f f f f 500 | 3006 115584 Space Hulk: Death Angel – The Card Game: Tyranid Enemy Pack http://cf.geekdo-images.com/images/pic1331840.jpg http://cf.geekdo-images.com/images/pic1331840_t.jpg 1 6 30 t 2011 0 7.49432999999999971 -1 0 -1 t f f f f f f f 501 | 3007 148228 Splendor http://cf.geekdo-images.com/images/pic1904079.jpg http://cf.geekdo-images.com/images/pic1904079_t.jpg 2 4 30 f 2014 0 7.58002999999999982 80 6 6 t f f f f f f f 502 | 3008 166384 Spyfall http://cf.geekdo-images.com/images/pic2453926.jpg http://cf.geekdo-images.com/images/pic2453926_t.jpg 3 8 15 f 2014 0 7.34881999999999991 215 0 -1 f f t f f f f f 503 | 3009 137269 Spyrium http://cf.geekdo-images.com/images/pic1808509.jpg http://cf.geekdo-images.com/images/pic1808509_t.jpg 2 5 75 f 2013 0 7.20891999999999999 365 1 6 f f f t f f f f 504 | 3010 147020 Star Realms http://cf.geekdo-images.com/images/pic1903816.jpg http://cf.geekdo-images.com/images/pic1903816_t.jpg 2 2 20 f 2014 0 7.68888999999999978 66 8 8 t f f f f f f f 505 | 3011 166702 Star Realms: Crisis – Bases & Battleships http://cf.geekdo-images.com/images/pic2255562.png http://cf.geekdo-images.com/images/pic2255562_t.png 2 6 -1 t 2014 0 7.77214999999999989 -1 0 -1 t f f f f f f f 506 | 3012 166707 Star Realms: Crisis – Events http://cf.geekdo-images.com/images/pic2255575.png http://cf.geekdo-images.com/images/pic2255575_t.png 2 6 -1 t 2014 0 7.16818000000000044 -1 0 -1 t f f f f f f f 507 | 3013 166704 Star Realms: Crisis – Fleets & Fortresses http://cf.geekdo-images.com/images/pic2255568.png http://cf.geekdo-images.com/images/pic2255568_t.png 2 6 -1 t 2014 0 7.76034999999999986 -1 0 -1 t f f f f f f f 508 | 3014 166708 Star Realms: Crisis – Heroes http://cf.geekdo-images.com/images/pic2255577.png http://cf.geekdo-images.com/images/pic2255577_t.png 2 6 -1 t 2014 0 7.09837999999999969 -1 0 -1 t f f f f f f f 509 | 3015 98242 Star Trek Deck Building Game: The Next Generation http://cf.geekdo-images.com/images/pic1158378.jpg http://cf.geekdo-images.com/images/pic1158378_t.jpg 2 5 60 f 2011 0 6.58513000000000037 1692 9 8 t f f f f f f f 510 | 3016 111502 Star Trek Deck Building Game: The Next Generation – The Next Phase http://cf.geekdo-images.com/images/pic1903299.jpg http://cf.geekdo-images.com/images/pic1903299_t.jpg 2 4 90 f 2012 0 6.9214500000000001 1960 0 8 t f f f f f f f 511 | 3017 122690 Star Trek Deck Building Game: The Original Series http://cf.geekdo-images.com/images/pic1537370.jpg http://cf.geekdo-images.com/images/pic1537370_t.jpg 2 4 90 f 2012 0 6.82216999999999985 2037 1 7 t f f f f f f f 512 | 3018 117985 Star Trek: Catan http://cf.geekdo-images.com/images/pic1365413.jpg http://cf.geekdo-images.com/images/pic1365413_t.jpg 3 4 75 f 2012 0 7.11247000000000007 678 1 -1 f f f f f f f f 513 | 3019 79131 Star Trek: Expeditions http://cf.geekdo-images.com/images/pic1021598.jpg http://cf.geekdo-images.com/images/pic1021598_t.jpg 1 4 60 f 2011 0 6.51729999999999965 1592 1 6 f f f t f f f f 514 | 3020 104413 Star Trek: Expeditions – Expansion Set http://cf.geekdo-images.com/images/pic1523049.jpg http://cf.geekdo-images.com/images/pic1523049_t.jpg 1 5 60 t 2012 0 6.65847999999999995 -1 0 -1 f f f t f f f f 515 | 3021 79127 Star Trek: Fleet Captains http://cf.geekdo-images.com/images/pic1081488.jpg http://cf.geekdo-images.com/images/pic1081488_t.jpg 2 4 75 f 2011 0 7.61406000000000027 270 1 8 t f f f f f f f 516 | 3022 182340 Star Trek: Frontiers http://cf.geekdo-images.com/images/pic3008535.jpg http://cf.geekdo-images.com/images/pic3008535_t.jpg 1 4 120 f 2016 0 9 -1 0 -1 f t f f f f f f 517 | 3023 171226 Starfighter http://cf.geekdo-images.com/images/pic2628590.jpg http://cf.geekdo-images.com/images/pic2628590_t.jpg 2 2 30 f 2015 0 7.40469000000000044 2274 0 -1 f f t f f f f f 518 | 3024 161614 Stockpile http://cf.geekdo-images.com/images/pic2537618.jpg http://cf.geekdo-images.com/images/pic2537618_t.jpg 2 5 45 f 2015 0 7.71227000000000018 547 3 8 t f f f f f f f 519 | 3025 34635 Stone Age http://cf.geekdo-images.com/images/pic1632539.jpg http://cf.geekdo-images.com/images/pic1632539_t.jpg 2 4 90 f 2008 0 7.64207000000000036 59 3 8 t f f f f f f f 520 | 3026 179460 Stronghold (2nd edition) http://cf.geekdo-images.com/images/pic2642989.jpg http://cf.geekdo-images.com/images/pic2642989_t.jpg 2 2 90 f 2015 0 8.03067000000000064 1613 0 -1 t f f f f f f f 521 | 3027 155173 Subdivision http://cf.geekdo-images.com/images/pic1961853.jpg http://cf.geekdo-images.com/images/pic1961853_t.jpg 1 4 45 f 2014 0 6.57643999999999984 1964 3 8 t f f f f f f f 522 | 3028 123260 Suburbia http://cf.geekdo-images.com/images/pic1418335.jpg http://cf.geekdo-images.com/images/pic1418335_t.jpg 1 4 90 f 2012 0 7.68895000000000017 63 14 8 t f f f f f f f 523 | 3029 145196 Suburbia Inc http://cf.geekdo-images.com/images/pic1722753.jpg http://cf.geekdo-images.com/images/pic1722753_t.jpg 1 4 90 t 2013 0 8.06564000000000014 -1 0 -1 t f f f f f f f 524 | 3030 58281 Summoner Wars http://cf.geekdo-images.com/images/pic2407328.jpg http://cf.geekdo-images.com/images/pic2407328_t.jpg 2 4 60 f 2009 0 7.36516999999999999 276 1 8 t f f f f f f f 525 | 3031 158889 Summoner Wars: Alliances Master Set http://cf.geekdo-images.com/images/pic2045253.jpg http://cf.geekdo-images.com/images/pic2045253_t.jpg 2 4 45 f 2015 0 8.48987999999999943 519 0 -1 t f f f f f f f 526 | 3032 82423 Summoner Wars: Fallen Kingdom Faction Deck http://cf.geekdo-images.com/images/pic891318.jpg http://cf.geekdo-images.com/images/pic891318_t.jpg 2 4 30 t 2010 0 7.99406999999999979 -1 0 -1 t f f f f f f f 527 | 3033 82420 Summoner Wars: Guild Dwarves vs Cave Goblins http://cf.geekdo-images.com/images/pic1218282.jpg http://cf.geekdo-images.com/images/pic1218282_t.jpg 2 4 30 f 2009 0 7.70645999999999987 280 0 -1 t f f f f f f f 528 | 3034 93260 Summoner Wars: Master Set http://cf.geekdo-images.com/images/pic923048.jpg http://cf.geekdo-images.com/images/pic923048_t.jpg 2 4 30 f 2011 0 7.68278999999999979 85 0 -1 t f f f f f f f 529 | 3035 113227 Summoner Wars: Mercenaries Faction Deck http://cf.geekdo-images.com/images/pic1224444.jpg http://cf.geekdo-images.com/images/pic1224444_t.jpg 2 4 30 t 2012 0 8.14738000000000007 -1 0 -1 t f f f f f f f 530 | 3036 82421 Summoner Wars: Phoenix Elves vs Tundra Orcs http://cf.geekdo-images.com/images/pic1222742.jpg http://cf.geekdo-images.com/images/pic1222742_t.jpg 2 4 30 f 2009 0 7.64186999999999994 289 0 -1 t f f f f f f f 531 | 3037 82422 Summoner Wars: Vanguards Faction Deck http://cf.geekdo-images.com/images/pic1426692.jpg http://cf.geekdo-images.com/images/pic1426692_t.jpg 2 4 30 t 2010 0 7.82183000000000028 -1 0 -1 t f f f f f f f 532 | 3038 2653 Survive: Escape from Atlantis! http://cf.geekdo-images.com/images/pic1300182.png http://cf.geekdo-images.com/images/pic1300182_t.png 2 4 60 f 2012 0 7.3404499999999997 182 2 7 t f f f f f f f 533 | 3039 96323 Survive: Escape from Atlantis! - 5-6 Player Mini Expansion http://cf.geekdo-images.com/images/pic1586886.jpg http://cf.geekdo-images.com/images/pic1586886_t.jpg 2 6 45 t 2011 0 7.39022000000000023 -1 0 -1 t f f f f f f f 534 | 3040 138745 Survive: Escape from Atlantis! - Dolphins & Dive Dice Mini Extension http://cf.geekdo-images.com/images/pic1586882.jpg http://cf.geekdo-images.com/images/pic1586882_t.jpg 2 4 60 t 2013 0 7.1925100000000004 -1 0 -1 t f f f f f f f 535 | 3041 177147 Survive: Space Attack! http://cf.geekdo-images.com/images/pic2506676.jpg http://cf.geekdo-images.com/images/pic2506676_t.jpg 2 4 60 f 2015 0 7.30187000000000008 3113 1 7 t f f f f f f f 536 | 3042 133473 Sushi Go! http://cf.geekdo-images.com/images/pic1900075.jpg http://cf.geekdo-images.com/images/pic1900075_t.jpg 2 5 15 f 2013 0 7.16542999999999974 287 3 6 t f f f f f f f 537 | 3043 146508 T.I.M.E Stories http://cf.geekdo-images.com/images/pic2617634.png http://cf.geekdo-images.com/images/pic2617634_t.png 2 4 90 f 2015 0 8.22724000000000011 27 5 8 t f f f f f f f 538 | 3044 179820 Tail Feathers http://cf.geekdo-images.com/images/pic2661822.jpg http://cf.geekdo-images.com/images/pic2661822_t.jpg 2 4 90 f 2015 0 7.98650000000000038 2596 0 -1 f t f f f f f f 539 | 3045 70919 Takenoko http://cf.geekdo-images.com/images/pic1912529.jpg http://cf.geekdo-images.com/images/pic1912529_t.jpg 2 4 45 f 2011 0 7.3620599999999996 162 5 8 t f f f f f f f 540 | 3046 34119 Tales of the Arabian Nights http://cf.geekdo-images.com/images/pic486114.jpg http://cf.geekdo-images.com/images/pic486114_t.jpg 1 6 120 f 2009 0 7.29891999999999985 236 1 6 t f f f f f f f 541 | 3047 118048 Targi http://cf.geekdo-images.com/images/pic1629318.jpg http://cf.geekdo-images.com/images/pic1629318_t.jpg 2 2 60 f 2012 0 7.60496999999999979 145 1 7 t f f f f f f f 542 | 3048 164072 Tenka: Shogun Edition http://cf.geekdo-images.com/images/pic2204892.jpg http://cf.geekdo-images.com/images/pic2204892_t.jpg 3 8 45 f 2014 0 6.69167000000000023 -1 0 -1 f f f t f f f f 543 | 3049 147242 A Terrible Time http://cf.geekdo-images.com/images/pic2094046.jpg http://cf.geekdo-images.com/images/pic2094046_t.jpg 4 16 45 f 2014 0 4.84999999999999964 -1 0 -1 f f f t f f f f 544 | 3050 120677 Terra Mystica http://cf.geekdo-images.com/images/pic1356616.jpg http://cf.geekdo-images.com/images/pic1356616_t.jpg 2 5 150 f 2012 0 8.28603000000000023 3 4 8 t f f f f f f f 545 | 3051 161317 Terra Mystica: Fire & Ice http://cf.geekdo-images.com/images/pic2227159.png http://cf.geekdo-images.com/images/pic2227159_t.png 2 5 100 t 2014 0 8.55733000000000033 -1 0 -1 t f f f f f f f 546 | 3052 182028 Through the Ages: A New Story of Civilization http://cf.geekdo-images.com/images/pic2663291.jpg http://cf.geekdo-images.com/images/pic2663291_t.jpg 2 4 240 f 2015 0 9.01654000000000089 4 1 8 t f f f f f f f 547 | 3053 25613 Through the Ages: A Story of Civilization http://cf.geekdo-images.com/images/pic236169.jpg http://cf.geekdo-images.com/images/pic236169_t.jpg 2 4 240 f 2006 0 8.16268999999999956 7 2 8 f f f t f f f f 548 | 3054 122967 Thunderstone Advance: Caverns of Bane http://cf.geekdo-images.com/images/pic1288215.jpg http://cf.geekdo-images.com/images/pic1288215_t.jpg 1 5 45 t 2012 0 7.84802 -1 0 -1 t f f f f f f f 549 | 3055 158727 Thunderstone Advance: Into the Abyss http://cf.geekdo-images.com/images/pic2020116.jpg http://cf.geekdo-images.com/images/pic2020116_t.jpg 1 5 60 t 2014 0 8.20701000000000036 -1 0 -1 t f f f f f f f 550 | 3056 142961 Thunderstone Advance: Numenera http://cf.geekdo-images.com/images/pic1683747.jpg http://cf.geekdo-images.com/images/pic1683747_t.jpg 1 5 90 f 2013 0 7.61615999999999982 701 0 7 t f f f f f f f 551 | 3057 131306 Thunderstone Advance: Root of Corruption http://cf.geekdo-images.com/images/pic1437796.jpg http://cf.geekdo-images.com/images/pic1437796_t.jpg 1 5 90 t 2012 0 7.86265999999999998 -1 0 -1 t f f f f f f f 552 | 3058 116998 Thunderstone Advance: Towers of Ruin http://cf.geekdo-images.com/images/pic1196537.jpg http://cf.geekdo-images.com/images/pic1196537_t.jpg 1 5 90 f 2012 0 7.55410000000000004 195 12 6 t f f f f f f f 553 | 3059 152765 Thunderstone Advance: Worlds Collide http://cf.geekdo-images.com/images/pic1888624.jpg http://cf.geekdo-images.com/images/pic1888624_t.jpg 1 5 60 f 2014 0 7.9929800000000002 746 0 -1 t f f f f f f f 554 | 3060 9209 Ticket to Ride http://cf.geekdo-images.com/images/pic38668.jpg http://cf.geekdo-images.com/images/pic38668_t.jpg 2 5 60 f 2004 0 7.49699000000000026 99 22 9 t f f f f f f f 555 | 3061 182078 Ticket to Ride Map Collection: Volume 5 – United Kingdom & Pennsylvania http://cf.geekdo-images.com/images/pic2640876.jpg http://cf.geekdo-images.com/images/pic2640876_t.jpg 2 5 75 t 2015 0 8.11857999999999969 -1 0 -1 t f f f f f f f 556 | 3062 160069 Ticket to Ride: 10th Anniversary http://cf.geekdo-images.com/images/pic1927856.jpg http://cf.geekdo-images.com/images/pic1927856_t.jpg 2 5 60 f 2014 0 8.35169999999999924 -1 0 8 t f f f f f f f 557 | 3063 106645 Ticket to Ride: India & Switzerland http://cf.geekdo-images.com/images/pic1077595.jpg http://cf.geekdo-images.com/images/pic1077595_t.jpg 2 4 60 t 2011 0 7.87786000000000008 -1 0 7 t f f f f f f f 558 | 3064 31627 Ticket to Ride: Nordic Countries http://cf.geekdo-images.com/images/pic369616.jpg http://cf.geekdo-images.com/images/pic369616_t.jpg 2 3 60 f 2007 0 7.67241000000000017 92 0 7 t f f f f f f f 559 | 3065 24439 Ticket to Ride: USA 1910 http://cf.geekdo-images.com/images/pic144599.jpg http://cf.geekdo-images.com/images/pic144599_t.jpg 2 5 60 t 2006 0 8.02203000000000088 -1 0 -1 t f f f f f f f 560 | 3066 176229 Tides of Time http://cf.geekdo-images.com/images/pic2486726.jpg http://cf.geekdo-images.com/images/pic2486726_t.jpg 2 2 20 f 2015 0 7.00765999999999956 643 1 7 t f f f f f f f 561 | 3067 42 Tigris & Euphrates http://cf.geekdo-images.com/images/pic2338267.jpg http://cf.geekdo-images.com/images/pic2338267_t.jpg 2 4 90 f 2015 0 7.74741000000000035 44 1 7 t f f f f f f f 562 | 3068 85256 Timeline: Inventions http://cf.geekdo-images.com/images/pic1724657.jpg http://cf.geekdo-images.com/images/pic1724657_t.jpg 2 8 15 f 2010 0 6.78444999999999965 717 1 6 t f f f f f f f 563 | 3069 155708 Tiny Epic Defenders http://cf.geekdo-images.com/images/pic2044766.jpg http://cf.geekdo-images.com/images/pic2044766_t.jpg 1 4 30 f 2015 0 6.50729999999999986 1543 1 7 f f f f f f f f 564 | 3070 123540 Tokaido http://cf.geekdo-images.com/images/pic1293719.jpg http://cf.geekdo-images.com/images/pic1293719_t.jpg 2 5 45 f 2012 0 7.06961999999999957 407 2 7 t f f f f f f f 565 | 3071 144384 Tokaido: Crossroads http://cf.geekdo-images.com/images/pic1717784.png http://cf.geekdo-images.com/images/pic1717784_t.png 2 5 45 t 2013 0 7.51822000000000035 -1 0 -1 t f f f f f f f 566 | 3072 124545 Town Center http://cf.geekdo-images.com/images/pic1834531.jpg http://cf.geekdo-images.com/images/pic1834531_t.jpg 2 4 30 f 2012 0 6.50495999999999963 2935 1 7 t f f f f f f f 567 | 3073 148319 Tragedy Looper http://cf.geekdo-images.com/images/pic2046290.png http://cf.geekdo-images.com/images/pic2046290_t.png 2 4 120 f 2011 0 7.47091999999999956 403 2 7 f f f t f f f f 568 | 3074 121408 Trains http://cf.geekdo-images.com/images/pic1606346.jpg http://cf.geekdo-images.com/images/pic1606346_t.jpg 2 4 45 f 2012 0 7.30621999999999971 268 8 8 t f f f f f f f 569 | 3075 146629 Trains: Gen Con 2013 http://cf.geekdo-images.com/images/pic1752832.jpg http://cf.geekdo-images.com/images/pic1752832_t.jpg 2 4 45 t 2013 0 7.48249999999999993 -1 0 -1 t f f f f f f f 570 | 3076 164213 Trains: Gen Con 2014 http://cf.geekdo-images.com/images/pic2320304.jpg http://cf.geekdo-images.com/images/pic2320304_t.jpg 2 2 -1 t 2014 0 7.72262999999999966 -1 0 -1 t f f f f f f f 571 | 3077 131324 Trains: Map Pack 1 – Germany/Northeastern USA http://cf.geekdo-images.com/images/pic2059136.jpg http://cf.geekdo-images.com/images/pic2059136_t.jpg 2 4 45 t 2012 0 7.53408999999999995 -1 0 -1 t f f f f f f f 572 | 3078 157001 Trains: Rising Sun http://cf.geekdo-images.com/images/pic2026066.jpg http://cf.geekdo-images.com/images/pic2026066_t.jpg 2 4 45 f 2014 0 7.70934000000000008 628 0 8 t f f f f f f f 573 | 3079 102680 Trajan http://cf.geekdo-images.com/images/pic1054375.jpg http://cf.geekdo-images.com/images/pic1054375_t.jpg 2 4 90 f 2011 0 7.82645999999999997 49 1 8 f f f t f f f f 574 | 3080 40688 Treasure Chest http://cf.geekdo-images.com/images/pic688564.jpg http://cf.geekdo-images.com/images/pic688564_t.jpg 2 6 75 t 2009 0 7.29532999999999987 -1 0 -1 t f f f f f f f 575 | 3081 163068 Trickerion: Legends of Illusion http://cf.geekdo-images.com/images/pic2585510.png http://cf.geekdo-images.com/images/pic2585510_t.png 2 4 120 f 2015 0 7.92105000000000015 384 1 7 t f f f f f f f 576 | 3082 148291 Trieste http://cf.geekdo-images.com/images/pic1791231.jpg http://cf.geekdo-images.com/images/pic1791231_t.jpg 3 3 25 f 2013 0 6.83586000000000027 1846 0 -1 f f f t f f f f 577 | 3083 16992 Tsuro http://cf.geekdo-images.com/images/pic875761.jpg http://cf.geekdo-images.com/images/pic875761_t.jpg 2 8 15 f 2004 0 6.67830000000000013 705 7 7 f f f t f f f f 578 | 3084 124172 Tsuro of the Seas http://cf.geekdo-images.com/images/pic1301853.jpg http://cf.geekdo-images.com/images/pic1301853_t.jpg 2 8 30 f 2012 0 6.65401000000000042 1002 1 7 t f f f f f f f 579 | 3085 147101 Tuscany: Expand the World of Viticulture http://cf.geekdo-images.com/images/pic1886437.jpg http://cf.geekdo-images.com/images/pic1886437_t.jpg 1 6 120 t 2014 0 8.65315999999999974 -1 0 9 t f f f f f f f 580 | 3086 56995 The Twelve Doctors: Doctor Who Card Game http://cf.geekdo-images.com/images/pic841416.jpg http://cf.geekdo-images.com/images/pic841416_t.jpg 2 2 30 f 2009 0 7.8377100000000004 2793 1 6 t f f f f f f f 581 | 3087 12333 Twilight Struggle http://cf.geekdo-images.com/images/pic361592.jpg http://cf.geekdo-images.com/images/pic361592_t.jpg 2 2 180 f 2005 0 8.35119000000000078 2 1 8 t f f f f f f f 582 | 3088 134352 Two Rooms and a Boom http://cf.geekdo-images.com/images/pic2335221.png http://cf.geekdo-images.com/images/pic2335221_t.png 6 30 15 f 2013 0 7.16342999999999996 699 2 6 t f f f f f f f 583 | 3089 126163 Tzolk'in: The Mayan Calendar http://cf.geekdo-images.com/images/pic1413480.jpg http://cf.geekdo-images.com/images/pic1413480_t.jpg 2 4 90 f 2012 0 7.97003000000000039 22 3 6 t f f f f f f f 584 | 3090 143065 Tzolk'in: The Mayan Calendar – Tribes & Prophecies http://cf.geekdo-images.com/images/pic1758469.png http://cf.geekdo-images.com/images/pic1758469_t.png 2 5 90 t 2013 0 8.12485999999999997 -1 0 -1 t f f f f f f f 585 | 3091 170561 Valeria: Card Kingdoms http://cf.geekdo-images.com/images/pic2919073.jpg http://cf.geekdo-images.com/images/pic2919073_t.jpg 1 5 45 f 2016 0 7.67077000000000009 1186 2 7 t f f f f f f f 586 | 3092 175223 Valley of the Kings: Afterlife http://cf.geekdo-images.com/images/pic2456554.jpg http://cf.geekdo-images.com/images/pic2456554_t.jpg 1 4 45 f 2015 0 7.60264999999999969 1254 1 7 t f f f f f f f 587 | 3093 157526 Viceroy http://cf.geekdo-images.com/images/pic2254354.jpg http://cf.geekdo-images.com/images/pic2254354_t.jpg 1 4 60 f 2014 0 7.1722999999999999 515 1 7 t f f f f f f f 588 | 3094 104006 Village http://cf.geekdo-images.com/images/pic2760568.jpg http://cf.geekdo-images.com/images/pic2760568_t.jpg 2 4 90 f 2011 0 7.58823999999999987 88 3 8 t f f f f f f f 589 | 3095 136223 Village Inn http://cf.geekdo-images.com/images/pic2760535.jpg http://cf.geekdo-images.com/images/pic2760535_t.jpg 2 5 120 t 2013 0 7.89770000000000039 -1 0 -1 t f f f f f f f 590 | 3096 155996 Village Port http://cf.geekdo-images.com/images/pic2760541.jpg http://cf.geekdo-images.com/images/pic2760541_t.jpg 2 5 90 t 2014 0 8.08449000000000062 -1 0 -1 t f f f f f f f 591 | 3097 128621 Viticulture http://cf.geekdo-images.com/images/pic2619743.jpg http://cf.geekdo-images.com/images/pic2619743_t.jpg 2 6 90 f 2013 0 7.86753000000000036 76 7 10 t f f f f f f f 592 | 3098 171623 The Voyages of Marco Polo http://cf.geekdo-images.com/images/pic2461346.png http://cf.geekdo-images.com/images/pic2461346_t.png 2 4 100 f 2015 0 8.03613 38 2 8 t f f f f f f f 593 | 3099 153905 Warband: Against the Darkness http://cf.geekdo-images.com/images/pic2060503.jpg http://cf.geekdo-images.com/images/pic2060503_t.jpg 2 5 60 f 2015 0 7.00121000000000038 3561 0 -1 t f f f f f f f 594 | 3100 151384 Warfighter Expansion #1: Reloading! http://cf.geekdo-images.com/images/pic1856022.jpg http://cf.geekdo-images.com/images/pic1856022_t.jpg 1 6 30 t 2014 0 7.71293999999999969 -1 0 -1 f f f t f f f f 595 | 3101 151385 Warfighter Expansion #2: Stealth http://cf.geekdo-images.com/images/pic1856023.jpg http://cf.geekdo-images.com/images/pic1856023_t.jpg 1 6 30 t 2014 0 7.68095000000000017 -1 0 -1 f f f t f f f f 596 | 3102 151386 Warfighter Expansion #3: Support http://cf.geekdo-images.com/images/pic1856021.jpg http://cf.geekdo-images.com/images/pic1856021_t.jpg 1 6 30 t 2014 0 7.75058999999999987 -1 0 -1 f f f t f f f f 597 | 3103 149951 Warfighter: The Tactical Special Forces Card Game http://cf.geekdo-images.com/images/pic1843359.jpg http://cf.geekdo-images.com/images/pic1843359_t.jpg 1 6 180 f 2014 0 7.91913999999999962 1003 1 7 f f f t f f f f 598 | 3104 156776 Warhammer 40,000: Conquest http://cf.geekdo-images.com/images/pic2079275.jpg http://cf.geekdo-images.com/images/pic2079275_t.jpg 2 2 45 f 2014 0 7.58087 454 6 7 t f f f f f f f 599 | 3105 181521 Warhammer Quest: The Adventure Card Game http://cf.geekdo-images.com/images/pic2625794.jpg http://cf.geekdo-images.com/images/pic2625794_t.jpg 1 4 60 f 2015 0 7.90428999999999959 368 3 8 t f f f f f f f 600 | 3106 150312 Welcome to the Dungeon http://cf.geekdo-images.com/images/pic2436689.jpg http://cf.geekdo-images.com/images/pic2436689_t.jpg 2 4 30 f 2013 0 6.79799000000000042 765 0 -1 f f f t f f f f 601 | 3107 20100 Wits & Wagers http://cf.geekdo-images.com/images/pic521431.jpg http://cf.geekdo-images.com/images/pic521431_t.jpg 3 7 25 f 2005 0 7.00830999999999982 410 2 8 t f f f f f f f 602 | 3108 66588 Wits & Wagers Family http://cf.geekdo-images.com/images/pic664569.jpg http://cf.geekdo-images.com/images/pic664569_t.jpg 3 10 20 f 2010 0 7.02195999999999998 979 0 8 t f f f f f f f 603 | 3109 123239 Wits & Wagers Party http://cf.geekdo-images.com/images/pic1285411.jpg http://cf.geekdo-images.com/images/pic1285411_t.jpg 4 18 25 f 2012 0 6.95188000000000006 1337 0 -1 t f f f f f f f 604 | 3110 163602 XCOM: The Board Game http://cf.geekdo-images.com/images/pic2247621.jpg http://cf.geekdo-images.com/images/pic2247621_t.jpg 1 4 90 f 2015 0 7.30044000000000004 348 2 7 f f f f f t f f 605 | 3111 159109 XenoShyft: Onslaught http://cf.geekdo-images.com/images/pic2015174.jpg http://cf.geekdo-images.com/images/pic2015174_t.jpg 1 4 60 f 2015 0 7.21586999999999978 776 1 6 f f f t f f f f 606 | 3112 113924 Zombicide http://cf.geekdo-images.com/images/pic1196191.jpg http://cf.geekdo-images.com/images/pic1196191_t.jpg 1 6 60 f 2012 0 7.38785999999999987 209 1 6 t f f f f f f f 607 | 3113 137988 Zombicide: Prison Outbreak http://cf.geekdo-images.com/images/pic1805937.jpg http://cf.geekdo-images.com/images/pic1805937_t.jpg 1 6 120 f 2013 0 7.67999000000000009 277 0 -1 t f f f f f f f 608 | 3114 137987 Zombicide: Toxic City Mall http://cf.geekdo-images.com/images/pic1805936.jpg http://cf.geekdo-images.com/images/pic1805936_t.jpg 1 6 60 t 2013 0 7.9126599999999998 -1 0 -1 t f f f f f f f 609 | 3115 62871 Zombie Dice http://cf.geekdo-images.com/images/pic2664015.jpg http://cf.geekdo-images.com/images/pic2664015_t.jpg 2 99 20 f 2010 0 6.26041000000000025 1459 13 6 t f f f f f f f 610 | \. 611 | 612 | 613 | -- 614 | -- Name: games_id_seq; Type: SEQUENCE SET; Schema: public; Owner: uriybbylwngrsv 615 | -- 616 | 617 | SELECT pg_catalog.setval('games_id_seq', 3115, true); 618 | 619 | 620 | -- 621 | -- Name: games_pkey; Type: CONSTRAINT; Schema: public; Owner: uriybbylwngrsv 622 | -- 623 | 624 | ALTER TABLE ONLY games 625 | ADD CONSTRAINT games_pkey PRIMARY KEY (id); 626 | 627 | 628 | -- 629 | -- Name: public; Type: ACL; Schema: -; Owner: uriybbylwngrsv 630 | -- 631 | 632 | REVOKE ALL ON SCHEMA public FROM PUBLIC; 633 | REVOKE ALL ON SCHEMA public FROM uriybbylwngrsv; 634 | GRANT ALL ON SCHEMA public TO uriybbylwngrsv; 635 | GRANT ALL ON SCHEMA public TO PUBLIC; 636 | 637 | 638 | -- 639 | -- PostgreSQL database dump complete 640 | -- 641 | 642 | --------------------------------------------------------------------------------