Skip to content

Commit

Permalink
Add remote env to users service
Browse files Browse the repository at this point in the history
  • Loading branch information
shanejearley committed Jul 5, 2023
1 parent 29440c7 commit cb5fc77
Show file tree
Hide file tree
Showing 8 changed files with 95 additions and 86 deletions.
93 changes: 53 additions & 40 deletions infrastructure/cdk/src/providers/users.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ import { UsersStackProps } from '../interfaces/StackProps'
import { Config } from './config'
import { kebabCase } from '@casimir/helpers'

// Todo add supertokens core or use their managed service (probably the latter for now)

/**
* Users API stack
*/
Expand All @@ -28,42 +30,7 @@ export class UsersStack extends cdk.Stack {
const { project, stage, rootDomain, subdomains } = config
const { certificate, hostedZone, vpc } = props

/** Build users service image */
const imageAsset = new ecrAssets.DockerImageAsset(this, config.getFullStackResourceName(this.name, 'image'), {
directory: this.contextPath,
file: this.assetPath,
platform: ecrAssets.Platform.LINUX_AMD64,
ignoreMode: cdk.IgnoreMode.GIT
})

/** Create a stage-specific ECS cluster */
const cluster = new ecs.Cluster(this, config.getFullStackResourceName(this.name, 'cluster'), {
vpc
})

/** Create a load-balanced users service */
const usersService = new ecsPatterns.ApplicationLoadBalancedFargateService(this, config.getFullStackResourceName(this.name, 'fargate'), {
assignPublicIp: true,
certificate,
cluster,
domainName: `${subdomains.users}.${rootDomain}`, // e.g. users.casimir.co or users.dev.casimir.co
domainZone: hostedZone,
taskImageOptions: {
containerPort: 4000,
image: ecs.ContainerImage.fromDockerImageAsset(imageAsset),
environment: {
PROJECT: project,
STAGE: stage
}
}
})

/** Override the default health check path */
usersService.targetGroup.configureHealthCheck({
path: '/health'
})

/** Create DB credentials */
/** Create the users DB credentials */
const dbCredentials = new secretsmanager.Secret(this, config.getFullStackResourceName(this.name, 'db-credentials'), {
secretName: kebabCase(config.getFullStackResourceName(this.name, 'db-credentials')),
generateSecretString: {
Expand All @@ -76,9 +43,6 @@ export class UsersStack extends cdk.Stack {
}
})

/** Grant users service access to DB credentials */
dbCredentials.grantRead(usersService.taskDefinition.taskRole)

/** Create a DB security group */
const dbSecurityGroup = new ec2.SecurityGroup(this, config.getFullStackResourceName(this.name, 'db-security-group'), {
vpc,
Expand All @@ -89,7 +53,7 @@ export class UsersStack extends cdk.Stack {
dbSecurityGroup.addIngressRule(ec2.Peer.anyIpv4(), ec2.Port.tcp(5432))

/** Create a DB cluster */
const dbCluster = new rds.DatabaseCluster(this, config.getFullStackResourceName(this.name, 'db-cluster'), {
new rds.DatabaseCluster(this, config.getFullStackResourceName(this.name, 'db-cluster'), {
engine: rds.DatabaseClusterEngine.auroraPostgres({
version: rds.AuroraPostgresEngineVersion.VER_15_2
}),
Expand All @@ -109,5 +73,54 @@ export class UsersStack extends cdk.Stack {
serverlessV2MinCapacity: 0.5,
serverlessV2MaxCapacity: 1
})

/** Create an ECS cluster */
const ecsCluster = new ecs.Cluster(this, config.getFullStackResourceName(this.name, 'cluster'), {
vpc
})

/** Build the users service image */
const imageAsset = new ecrAssets.DockerImageAsset(this, config.getFullStackResourceName(this.name, 'image'), {
directory: this.contextPath,
file: this.assetPath,
platform: ecrAssets.Platform.LINUX_AMD64,
ignoreMode: cdk.IgnoreMode.GIT
})

/** Get the sessions credentials */
const sessionsCredentials = secretsmanager.Secret.fromSecretNameV2(this, config.getFullStackResourceName(this.name, 'sessions-credentials'), kebabCase(config.getFullStackResourceName(this.name, 'sessions-credentials')))

/** Create a load-balanced users service */
const fargateService = new ecsPatterns.ApplicationLoadBalancedFargateService(this, config.getFullStackResourceName(this.name, 'fargate'), {
assignPublicIp: true,
certificate,
cluster: ecsCluster,
domainName: `${subdomains.users}.${rootDomain}`, // e.g. users.casimir.co or users.dev.casimir.co
domainZone: hostedZone,
taskImageOptions: {
containerPort: 4000,
image: ecs.ContainerImage.fromDockerImageAsset(imageAsset),
environment: {
PROJECT: project,
STAGE: stage,
USERS_URL: `https://${subdomains.users}.${rootDomain}`,
WEB_URL: `https://${subdomains.web}.${rootDomain}`
},
secrets: {
DB_HOST: ecs.Secret.fromSecretsManager(dbCredentials, 'host'),
DB_PORT: ecs.Secret.fromSecretsManager(dbCredentials, 'port'),
DB_NAME: ecs.Secret.fromSecretsManager(dbCredentials, 'dbname'),
DB_USER: ecs.Secret.fromSecretsManager(dbCredentials, 'username'),
DB_PASSWORD: ecs.Secret.fromSecretsManager(dbCredentials, 'password'),
SESSIONS_HOST: ecs.Secret.fromSecretsManager(sessionsCredentials, 'host'),
SESSIONS_KEY: ecs.Secret.fromSecretsManager(sessionsCredentials, 'key')
}
}
})

/** Override the default health check path */
fargateService.targetGroup.configureHealthCheck({
path: '/health'
})
}
}
2 changes: 1 addition & 1 deletion scripts/local/dev.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ void async function () {

/** Set project-wide variables */
process.env.PROJECT = process.env.PROJECT || 'casimir'
process.env.STAGE = process.env.STAGE || 'dev'
process.env.STAGE = process.env.STAGE || 'local'

/** Pass stage to web app */
process.env.PUBLIC_STAGE = process.env.STAGE
Expand Down
17 changes: 6 additions & 11 deletions scripts/migrations/users.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,14 @@ import { getSecret, run } from '@casimir/helpers'
void async function () {
const project = process.env.PROJECT || 'casimir'
const stage = process.env.STAGE || 'dev'
const service = 'users'
const dbName = 'users'

/** Load DB credentials */
const dbCredentials = await getSecret(`${project}-${service}-db-credentials-${stage}`)
console.log('dbCredentials', dbCredentials)
const dbCredentials = await getSecret(`${project}-${dbName}-db-credentials-${stage}`)

/** Parse DB credentials */
const { dbname, port, host, username, password } = JSON.parse(dbCredentials as string)
const pgUrl = `postgres://${username}:${password}@${host}:${port}/${dbname}`
const { port, host, username, password } = JSON.parse(dbCredentials as string)
const pgUrl = `postgres://${username}:${password}@${host}:${port}/${dbName}`

/** Resource path from package caller */
const resourcePath = './scripts'
Expand Down Expand Up @@ -43,12 +42,8 @@ void async function () {
fs.writeFileSync(`${sqlDir}/schema.sql`, sqlSchema)

const atlasCli = await run('which atlas')
if (!atlasCli) {
if (os.platform() === 'darwin') {
await run('echo y | brew install atlas')
} else {
await run('curl -sSf https://atlasgo.sh | sh')
}
if (!atlasCli && os.platform() === 'darwin') {
await run('echo y | brew install atlas')
}
await run(`atlas schema apply --url "${pgUrl}?sslmode=disable" --to "file://${sqlDir}/schema.sql" --dev-url "docker://postgres/15" --auto-approve`)
}()
27 changes: 20 additions & 7 deletions services/users/scripts/dev.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,29 @@
import fs from 'fs'
import os from 'os'
import { run } from '@casimir/helpers'
import { run, getSecret, loadCredentials } from '@casimir/helpers'
import { JsonSchema, Schema, accountSchema, nonceSchema, userSchema, userAccountSchema } from '@casimir/data'

/**
* Run a local users database and service
*/
void async function () {

/** Load credentials and get secrets for non-local config */
if (process.env.STAGE !== 'local') {
await loadCredentials()
const dbCredentials = await getSecret(`${process.env.PROJECT}-users-db-credentials-${process.env.STAGE}`)
const { port: dbPort, host: dbHost, dbname: dbName, username: dbUser, password: dbPassword } = JSON.parse(dbCredentials as string)
process.env.DB_HOST = dbHost
process.env.DB_PORT = dbPort
process.env.DB_NAME = dbName
process.env.DB_USER = dbUser
process.env.DB_PASSWORD = dbPassword
const sessionsCredentials = await getSecret(`${process.env.PROJECT}-sessions-credentials-${process.env.STAGE}`)
const { host: sessionsHost, key: sessionsKey } = JSON.parse(sessionsCredentials as string)
process.env.SESSIONS_HOST = sessionsHost
process.env.SESSIONS_KEY = sessionsKey
}

/** Resource path from package caller */
const resourcePath = './scripts'

Expand Down Expand Up @@ -43,12 +60,8 @@ void async function () {
await new Promise(resolve => setTimeout(resolve, 2500))
}
const atlasCli = await run('which atlas')
if (!atlasCli) {
if (os.platform() === 'darwin') {
await run('echo y | brew install atlas')
} else {
await run('curl -sSf https://atlasgo.sh | sh')
}
if (!atlasCli && os.platform() === 'darwin') {
await run('echo y | brew install atlas')
}
await run(`atlas schema apply --url "postgres://postgres:password@localhost:5432/users?sslmode=disable" --to "file://${sqlDir}/schema.sql" --dev-url "docker://postgres/15" --auto-approve`)

Expand Down
2 changes: 1 addition & 1 deletion services/users/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ app.use(express.json())
/** CORS needs explicit origin (no *) with credentials:true */
app.use(
cors({
origin: 'http://localhost:3001',
origin: process.env.WEB_URL || 'http://localhost:3001',
allowedHeaders: ['content-type', ...supertokens.getAllCORSHeaders()],
methods: ['GET', 'PUT', 'POST', 'DELETE'],
credentials: true
Expand Down
11 changes: 5 additions & 6 deletions services/users/src/providers/db.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,11 @@ import useEthers from './ethers'
const { generateNonce } = useEthers()

const postgres = new Postgres({
// These will become environment variables
host: 'localhost',
port: 5432,
database: 'users',
user: 'admin',
password: 'password'
host: process.env.DB_HOST || 'localhost',
port: parseInt(process.env.DB_PORT as string) || 5432,
database: process.env.DB_NAME || 'users',
user: process.env.DB_USER || 'postgres',
password: process.env.DB_PASSWORD || 'password'
})

export default function useDB() {
Expand Down
18 changes: 3 additions & 15 deletions services/users/src/providers/postgres.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,9 @@ export class Postgres {

/**
* Create a new Postgres database provider
* @param {PoolConfig} config - Postgres connection pool config
* @example
* ```ts
* const postgres = new Postgres()
* ```
*/
constructor(config?: PoolConfig) {
this.pool = new Pool(config)
constructor(poolConfig: PoolConfig) {
this.pool = new Pool(poolConfig)
}

/**
Expand All @@ -31,18 +26,11 @@ export class Postgres {
* if (rows.length) console.log(rows[0].text) // Hello world!
* ```
*/
async query(text: string, params: any[] = []) { // Todo - use union of stricter @casimir/types for params
async query(text: string, params: any[] = []) {
const client = await this.pool.connect()
const res = await client.query(text, params)
client.release()
const { rows } = res
return rows
}

/**
* Close the connection pool (when the server exits)
*/
async close() {
await this.pool.end()
}
}
11 changes: 6 additions & 5 deletions services/users/src/sessions.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,15 @@ export const SuperTokensBackendConfig: TypeInput = {
framework: 'express',
supertokens: {
// SuperTokens core (temporary) host
connectionURI: 'https://try.supertokens.com',
connectionURI: process.env.SESSIONS_HOST || 'https://try.supertokens.com',
apiKey: process.env.SESSIONS_KEY || ''
},
appInfo: {
appName: 'Casimir',
apiDomain: 'http://localhost:4000',
websiteDomain: 'http://localhost:3001',
appName: process.env.PROJECT || 'casimir',
apiDomain: process.env.USERS_URL || 'http://localhost:4000',
websiteDomain: process.env.WEB_URL || 'http://localhost:3001'
},
recipeList: [
Session.init(),
],
]
}

0 comments on commit cb5fc77

Please sign in to comment.