Initial structure

This commit is contained in:
Chris Anderson 2022-07-07 21:37:16 -05:00
parent 13ed958266
commit e5de3090e8
16 changed files with 8984 additions and 0 deletions

1
.gitignore vendored
View file

@ -10,6 +10,7 @@ lerna-debug.log*
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
# Runtime data
.DS_Store
pids
*.pid
*.seed

11
db/migrations/init.js Normal file
View file

@ -0,0 +1,11 @@
exports.up = function(knex) {
return knex.schema
.createTable('users', function (table) {
table.increments()
table.string('name', 255).defaultTo('')
})
}
exports.down = function(knex) {
}

3
global.d.ts vendored Normal file
View file

@ -0,0 +1,3 @@
declare module 'handlebars-utils' {
export function value(val: any, context: any, options: any)
}

8462
package-lock.json generated Normal file

File diff suppressed because it is too large Load diff

31
package.json Normal file
View file

@ -0,0 +1,31 @@
{
"name": "parcelvoy",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "ISC",
"devDependencies": {
"@types/nodemailer": "^6.4.4",
"@types/koa__router": "^8.0.11",
"typescript": "^4.7.4"
},
"dependencies": {
"@aws-sdk/client-ses": "^3.121.0",
"@aws-sdk/client-sns": "^3.121.0",
"@aws-sdk/client-sqs": "^3.121.0",
"@koa/router": "^11.0.1",
"dotenv": "^16.0.1",
"handlebars": "^4.7.7",
"handlebars-helpers": "^0.10.0",
"handlebars-utils": "^1.0.6",
"knex": "^2.1.0",
"koa": "^2.13.4",
"mysql2": "^2.3.3",
"nodemailer": "^6.7.6",
"sqs-consumer": "^5.7.0"
}
}

22
src/app.ts Normal file
View file

@ -0,0 +1,22 @@
import db, { Knex } from './config/database'
import env from './config/env'
export default class App {
db: Knex
private static $main: App
static get main() {
if (!App.$main) {
App.$main = new App()
}
return App.$main
}
constructor() {
this.db = db()
}
listen() {
}
}

18
src/config/database.ts Normal file
View file

@ -0,0 +1,18 @@
import knex, { Knex } from 'knex'
export { Knex }
interface DatabaseConfig {
host: string
port: number
user: string
password: string
database: string
}
export default (config: DatabaseConfig) => {
return knex({
client: 'mysql',
connection: config
})
}

19
src/config/env.ts Normal file
View file

@ -0,0 +1,19 @@
import * as dotenv from 'dotenv'
dotenv.config()
interface Env {
}
const env = {
db: {
host: process.env.DB_HOST!,
username: process.env.DB_USERNAME!,
password: process.env.DB_PASSWORD!,
port: process.env.DB_PORT!
}
}
// Check to ensure ENV exists
export default env

View file

@ -0,0 +1,11 @@
import Router from '@koa/router'
const router = new Router({
prefix: '/users'
})
router.get('/', (ctx, next) => {
})
export default router

6
src/controllers/index.ts Normal file
View file

@ -0,0 +1,6 @@
import Koa from 'koa'
import UserController from './UserController'
export default (api: Koa) => {
api.use(UserController.routes())
}

21
src/index.ts Normal file
View file

@ -0,0 +1,21 @@
import App from './app'
import env from './config/env'
App.init(env).listen();
// Migrate
// Validating settings
// Connect to database
const app = App.init(env)
app.listen()
api(app)
worker(app)
// Starting the API
// Starting job queue
//

0
src/migrate.ts Normal file
View file

27
src/models/User.ts Normal file
View file

@ -0,0 +1,27 @@
export interface Device {
token: string;
os: string;
model: string;
app_build: string;
app_version: string;
}
export interface User {
id: number;
project_id: number;
external_id: string;
email?: string;
phone?: string;
devices: Device[];
data: Record<string, any>; // first_name, last_name live in data
attributes: UserAttribute[]; //???
created_at: Date;
updated_at: Date;
}
export interface UserAttribute {
id: number;
user_id: number;
key: string;
value: any;
}

View file

@ -0,0 +1,203 @@
/**
* Appends the speficied `suffix to the given `string`.
*/
export const append = function (str: string, suffix: string) {
if (isString(str) && isString(suffix)) {
return str + suffix
}
return str
}
/**
* camelCase the characters in the given `string`.
*/
export const camelcase = function (str: string): string {
if (!isString(str)) return ''
return str.replace(/(?:^\w|[A-Z]|\b\w)/g, function(word, index) {
return index === 0 ? word.toLowerCase() : word.toUpperCase()
}).replace(/\s+/g, '')
}
/**
* Capitalize the first word in a sentence.
*/
export const capitalize = function (str: string): string {
if (!isString(str)) return ''
return str.charAt(0).toUpperCase() + str.slice(1)
}
/**
* Capitalize all words in a string.
*/
export const capitalizeAll = function (str: string): string {
if (!isString(str)) return ''
return str.replace(/\w\S*/g, function (word) {
return capitalize(word)
})
}
/**
* Truncates a string to the specified `length`, and appends
* it with an elipsis, ``.
*/
export const ellipsis = function (str: string, limit: number): string {
if (!isString(str)) return ''
if (str.length <= limit) {
return str
}
return truncate(str, limit) + '…'
}
/**
* Return true if `value` is a string.
*/
export const isString = function (value: any): boolean {
return typeof value === 'string'
}
/**
* Lowercase all characters in the given string.
*/
export const lowercase = function (str: string): string {
if (!isString(str)) return ''
return str.toLowerCase()
}
/**
* Return the number of occurrences of `substring` within the
* given `string`.
*/
export const occurrences = function (str: string, substring: string): number {
if (!isString(str) || !isString(str)) return 0
var len = substring.length
var pos = 0
var n = 0
while ((pos = str.indexOf(substring, pos)) > -1) {
n++
pos += len
}
return n
}
/**
* Prepends the given `string` with the specified `prefix`.
*/
export const prepend = function (str: string, prefix: string): string {
return isString(str) && isString(prefix)
? (prefix + str)
: str
}
/**
* Replace all occurrences of substring `a` with substring `b`.
*/
export const replace = function (str: string, a: string, b: string): string {
if (!isString(str)) return ''
if (!isString(a)) return str
if (!isString(b)) b = ''
return str.split(a).join(b)
}
/**
* Replace the first occurrence of substring `a` with substring `b`.
*/
export const replaceFirst = function (str: string, a: string, b: string): string {
if (!isString(str)) return ''
if (!isString(a)) return str
if (!isString(b)) b = ''
return str.replace(a, b)
}
/**
* Reverse a string.
*/
export const reverse = function (str: string): string {
if (!isString(str)) return ''
return str.split('').reverse().join('')
}
/**
* snake_case the characters in the given `string`.
*/
export const snakecase = function (str: string): string {
if (!isString(str)) return ''
return str.replace(/[A-Z]/g, (letter, index) => {
return index == 0 ? letter.toLowerCase() : '_'+ letter.toLowerCase()
})
}
/**
* Split `string` by the given `character`.
*/
export const split = function (str: string, character: string): string[] {
if (!isString(str)) return [str]
if (!isString(character)) character = ','
return str.split(character)
}
/**
* Tests whether a string begins with the given prefix.
*/
export const startsWith = function (prefix: string, str: string): boolean {
return isString(str) && str.startsWith(prefix)
}
/**
* Title case the given string.
*/
export const titleize = function (str: string): string {
if (!isString(str)) return ''
var title = str.replace(/[- _]+/g, ' ')
var words = title.split(' ')
var len = words.length
var res = []
var i = 0
while (len--) {
var word = words[i++]
res.push(capitalize(word))
}
return res.join(' ')
}
/**
* Removes extraneous whitespace from the beginning and end
* of a string.
*/
export const trim = function (str: string) {
return isString(str) ? str.trim() : ''
}
/**
* Truncate a string to the specified `length`
*/
export const truncate = function (str: string, limit: number, suffix: string = ''): string {
if (!isString(str)) return ''
if (!isString(suffix)) suffix = ''
if (str.length > limit) {
return str.slice(0, limit - suffix.length) + suffix
}
return str
}
/**
* Truncate a string to have the specified number of words.
*/
export const truncateWords = function (str: string, count: number, suffix = ''): string {
if (!isString(str) || typeof count !== 'number') return ''
if (!isString(suffix)) {
suffix = '…'
}
var num = Number(count)
var arr = str.split(/[ \t]/)
if (num > arr.length) {
arr = arr.slice(0, num)
}
var val = arr.join(' ').trim()
return val + suffix
}

46
src/render/Render.ts Normal file
View file

@ -0,0 +1,46 @@
import Handlebars from 'handlebars'
import * as StrHelpers from './Helpers/String'
export interface TemplateUser {
id: string
email?: string
phone?: string
}
export interface Variables {
user: TemplateUser
event?: Record<string, any>
}
const keys = Object.keys(StrHelpers)
const values = Object.values(StrHelpers)
for (const [i, v] of keys.entries()) {
Handlebars.registerHelper(keys[i], values[i])
}
/**
* Additional helpers that we should have:
*
* lt
* gt
* lte
* gte
* defaultIfEmpty
* replace
* truncate
* concat (use plus sign)
* urlEncode
* numberFormat - Take into account I18N | value, type (currency, percent), locale, digits, rounding mode
* math - addition, subtraction, multiplication
* join - Concatenate values in an array
* ifContains - Check if array contains
* first - First item in array
* last - Last item in array
* dateFormat - date, format (full, long, medium, short, string), tz
* dateMath - date, math string | Operands +1y-1M+3w-17d+7h+1m-50s | "now" corresponds to current date <-- hate this, can we just use math?
* now - current date
*/
export default (template: string, variables: Variables) => {
return Handlebars.compile(template)(variables)
}

103
tsconfig.json Normal file
View file

@ -0,0 +1,103 @@
{
"compilerOptions": {
/* Visit https://aka.ms/tsconfig to read more about this file */
/* Projects */
// "incremental": true, /* Save .tsbuildinfo files to allow for incremental compilation of projects. */
// "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */
// "tsBuildInfoFile": "./.tsbuildinfo", /* Specify the path to .tsbuildinfo incremental compilation file. */
// "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects. */
// "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */
// "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */
/* Language and Environment */
"target": "es2016", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */
// "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */
// "jsx": "preserve", /* Specify what JSX code is generated. */
// "experimentalDecorators": true, /* Enable experimental support for TC39 stage 2 draft decorators. */
// "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */
// "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h'. */
// "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */
// "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using 'jsx: react-jsx*'. */
// "reactNamespace": "", /* Specify the object invoked for 'createElement'. This only applies when targeting 'react' JSX emit. */
// "noLib": true, /* Disable including any library files, including the default lib.d.ts. */
// "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */
// "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */
/* Modules */
"module": "commonjs", /* Specify what module code is generated. */
// "rootDir": "./", /* Specify the root folder within your source files. */
// "moduleResolution": "node", /* Specify how TypeScript looks up a file from a given module specifier. */
"baseUrl": "./src", /* Specify the base directory to resolve non-relative module names. */
// "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */
// "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */
// "typeRoots": [], /* Specify multiple folders that act like './node_modules/@types'. */
// "types": [], /* Specify type package names to be included without being referenced in a source file. */
// "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */
// "moduleSuffixes": [], /* List of file name suffixes to search when resolving a module. */
// "resolveJsonModule": true, /* Enable importing .json files. */
// "noResolve": true, /* Disallow 'import's, 'require's or '<reference>'s from expanding the number of files TypeScript should add to a project. */
/* JavaScript Support */
// "allowJs": true, /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */
// "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */
// "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from 'node_modules'. Only applicable with 'allowJs'. */
/* Emit */
// "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */
// "declarationMap": true, /* Create sourcemaps for d.ts files. */
// "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */
// "sourceMap": true, /* Create source map files for emitted JavaScript files. */
// "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If 'declaration' is true, also designates a file that bundles all .d.ts output. */
"outDir": "./build", /* Specify an output folder for all emitted files. */
// "removeComments": true, /* Disable emitting comments. */
// "noEmit": true, /* Disable emitting files from a compilation. */
// "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */
// "importsNotUsedAsValues": "remove", /* Specify emit/checking behavior for imports that are only used for types. */
// "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */
// "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */
// "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */
// "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */
// "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */
// "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */
// "newLine": "crlf", /* Set the newline character for emitting files. */
// "stripInternal": true, /* Disable emitting declarations that have '@internal' in their JSDoc comments. */
// "noEmitHelpers": true, /* Disable generating custom helper functions like '__extends' in compiled output. */
// "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */
// "preserveConstEnums": true, /* Disable erasing 'const enum' declarations in generated code. */
// "declarationDir": "./", /* Specify the output directory for generated declaration files. */
// "preserveValueImports": true, /* Preserve unused imported values in the JavaScript output that would otherwise be removed. */
/* Interop Constraints */
// "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */
// "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */
"esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */
// "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */
"forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */
/* Type Checking */
"strict": true, /* Enable all strict type-checking options. */
// "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied 'any' type. */
// "strictNullChecks": true, /* When type checking, take into account 'null' and 'undefined'. */
// "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */
// "strictBindCallApply": true, /* Check that the arguments for 'bind', 'call', and 'apply' methods match the original function. */
// "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */
// "noImplicitThis": true, /* Enable error reporting when 'this' is given the type 'any'. */
// "useUnknownInCatchVariables": true, /* Default catch clause variables as 'unknown' instead of 'any'. */
// "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */
// "noUnusedLocals": true, /* Enable error reporting when local variables aren't read. */
// "noUnusedParameters": true, /* Raise an error when a function parameter isn't read. */
// "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */
// "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */
// "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */
// "noUncheckedIndexedAccess": true, /* Add 'undefined' to a type when accessed using an index. */
// "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */
// "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type. */
// "allowUnusedLabels": true, /* Disable error reporting for unused labels. */
// "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */
/* Completeness */
// "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */
"skipLibCheck": true /* Skip type checking all .d.ts files. */
}
}