npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

@studion/infra-code-blocks

v0.6.11

Published

Studion common infra components

Downloads

424

Readme

@studion/infra-code-blocks

Studion Platform common infra components.

Table of Contents

  1. Prerequisites
  2. Installation
  3. Usage
  4. API

Prerequisites

  • Working Pulumi project
  • AWS account with necessary permissions for each Studion component

Installation

  • Run the command:
$ npm i @studion/infra-code-blocks

Usage

  • Import Studion infra components in your project
import * as studion from '@studion/infra-code-blocks';
  • Use Studion components
import * as studion from '@studion/infra-code-blocks';

const project = new studion.Project('demo-project', {
  services: [
    {
      type: 'REDIS',
      serviceName: 'redis',
      dbName: 'test-db',
    },
  ],
});

export const projectName = project.name;
  • Deploy Pulumi stack
$ pulumi up

API

  1. Project
  2. Database
  3. Database Replica
  4. Redis
  5. StaticSite
  6. WebServer
  7. Nuxt SSR
  8. Mongo
  9. EcsService

Project

Project component makes it easy to spin up project infrastructure, hiding infrastructure complexity. The component creates its own VPC used for resources within the project. Services are created only if specified in the services list. If services is an empty list, VPC is the only service created by default.

new Project(name: string, args: ProjectArgs, opts?: pulumi.CustomResourceOptions);

| Argument | Description | | :------- | :--------------------------------------------: | | name * | The unique name of the resource. | | args * | The arguments to resource properties. | | opts | Bag of options to control resource's behavior. |

type ProjectArgs = {
  services: (
    | DatabaseServiceOptions
    | RedisServiceOptions
    | StaticSiteServiceOptions
    | WebServerServiceOptions
    | NuxtSSRServiceOptions
    | MongoServiceOptions
    | EcsServiceOptions
  )[];
  enableSSMConnect?: pulumi.Input<boolean>;
  numberOfAvailabilityZones?: number;
};

| Argument | Description | | :--------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------: | | services * | Service list. | | enableSSMConnect | Set up ec2 instance and SSM in order to connect to the database in the private subnet. Please refer to the SSM Connect section for more info. | | numberOfAvailabilityZones | Default is 2 which is recommended. If building a dev server, we can reduce to 1 availability zone to reduce hosting cost. |

type DatabaseServiceOptions = {
  type: 'DATABASE';
  serviceName: string;
  dbName: pulumi.Input<string>;
  username: pulumi.Input<string>;
  password?: pulumi.Input<string>;
  multiAz?: pulumi.Input<boolean>;
  applyImmediately?: pulumi.Input<boolean>;
  skipFinalSnapshot?: pulumi.Input<boolean>;
  allocatedStorage?: pulumi.Input<number>;
  maxAllocatedStorage?: pulumi.Input<number>;
  instanceClass?: pulumi.Input<string>;
  enableMonitoring?: pulumi.Input<boolean>;
  parameterGroupName?: pulumi.Input<string>;
  tags?: pulumi.Input<{
    [key: string]: pulumi.Input<string>;
  }>;
};
export type RedisServiceOptions = {
  type: 'REDIS';
  serviceName: string;
  dbName: pulumi.Input<string>;
  region?: pulumi.Input<string>;
};
export type StaticSiteServiceOptions = {
  type: 'STATIC_SITE';
  serviceName: string;
  domain?: pulumi.Input<string>;
  hostedZoneId?: pulumi.Input<string>;
  tags?: pulumi.Input<{
    [key: string]: pulumi.Input<string>;
  }>;
};
export type WebServerServiceOptions = {
  type: 'WEB_SERVER';
  serviceName: string;
  image: pulumi.Input<string>;
  port: pulumi.Input<number>;
  domain?: pulumi.Input<string>;
  hostedZoneId?: pulumi.Input<string>;
  environment?:
    | aws.ecs.KeyValuePair[]
    | ((services: Services) => aws.ecs.KeyValuePair[]);
  secrets?: aws.ecs.Secret[] | ((services: Services) => aws.ecs.Secret[]);
  desiredCount?: pulumi.Input<number>;
  autoscaling?: pulumi.Input<{
    enabled: pulumi.Input<boolean>;
    minCount?: pulumi.Input<number>;
    maxCount?: pulumi.Input<number>;
  }>;
  size?: pulumi.Input<Size>;
  healthCheckPath?: pulumi.Input<string>;
  taskExecutionRoleInlinePolicies?: pulumi.Input<
    pulumi.Input<RoleInlinePolicy>[]
  >;
  taskRoleInlinePolicies?: pulumi.Input<pulumi.Input<RoleInlinePolicy>[]>;
  tags?: pulumi.Input<{
    [key: string]: pulumi.Input<string>;
  }>;
};
export type NuxtSSRServiceOptions = {
  type: 'NUXT_SSR';
  serviceName: string;
  image: pulumi.Input<string>;
  port: pulumi.Input<number>;
  domain?: pulumi.Input<string>;
  hostedZoneId?: pulumi.Input<string>;
  environment?:
    | aws.ecs.KeyValuePair[]
    | ((services: Services) => aws.ecs.KeyValuePair[]);
  secrets?: aws.ecs.Secret[] | ((services: Services) => aws.ecs.Secret[]);
  desiredCount?: pulumi.Input<number>;
  autoscaling?: pulumi.Input<{
    enabled: pulumi.Input<boolean>;
    minCount?: pulumi.Input<number>;
    maxCount?: pulumi.Input<number>;
  }>;
  size?: pulumi.Input<Size>;
  healthCheckPath?: pulumi.Input<string>;
  taskExecutionRoleInlinePolicies?: pulumi.Input<
    pulumi.Input<RoleInlinePolicy>[]
  >;
  taskRoleInlinePolicies?: pulumi.Input<pulumi.Input<RoleInlinePolicy>[]>;
  tags?: pulumi.Input<{
    [key: string]: pulumi.Input<string>;
  }>;
};
type MongoServiceOptions = {
  type: 'MONGO';
  serviceName: string;
  username: pulumi.Input<string>;
  password?: pulumi.Input<string>;
  port?: pulumi.Input<number>;
  size?: pulumi.Input<Size>;
  tags?: pulumi.Input<{
    [key: string]: pulumi.Input<string>;
  }>;
};
type EcsServiceOptions = {
  type: 'ECS_SERVICE';
  serviceName: string;
  image: pulumi.Input<string>;
  port: pulumi.Input<number>;
  enableServiceAutoDiscovery: pulumi.Input<boolean>;
  lbTargetGroupArn?: aws.lb.TargetGroup['arn'];
  persistentStorageVolumePath?: pulumi.Input<string>;
  securityGroup?: aws.ec2.SecurityGroup;
  assignPublicIp?: pulumi.Input<boolean>;
  dockerCommand?: pulumi.Input<string[]>;
  environment?:
    | aws.ecs.KeyValuePair[]
    | ((services: Services) => aws.ecs.KeyValuePair[]);
  secrets?: aws.ecs.Secret[] | ((services: Services) => aws.ecs.Secret[]);
  desiredCount?: pulumi.Input<number>;
  autoscaling?: pulumi.Input<{
    enabled: pulumi.Input<boolean>;
    minCount?: pulumi.Input<number>;
    maxCount?: pulumi.Input<number>;
  }>;
  size?: pulumi.Input<Size>;
  healthCheckPath?: pulumi.Input<string>;
  taskExecutionRoleInlinePolicies?: pulumi.Input<
    pulumi.Input<RoleInlinePolicy>[]
  >;
  taskRoleInlinePolicies?: pulumi.Input<pulumi.Input<RoleInlinePolicy>[]>;
  tags?: pulumi.Input<{
    [key: string]: pulumi.Input<string>;
  }>;
};

Often, web server depends on other services such as database, Redis, etc. For that purpose, environment factory can be used. The factory function receives services bag as argument.

const project = new studion.Project('demo-project', {
  services: [
    {
      type: 'REDIS',
      serviceName: 'redis',
      dbName: 'test-db',
    },
    {
      type: 'WEB_SERVER',
      serviceName: 'api',
      image: imageUri,
      port: 3000,
      domain: 'api.my-domain.com',
      hostedZoneId: 'my-domain.com-hostedZoneId',
      environment: (services: Services) => {
        const redisServiceName = 'redis';
        const redis = services[redisServiceName];
        return [
          { name: 'REDIS_HOST', value: redis.endpoint },
          { name: 'REDIS_PORT', value: redis.port.apply(port => String(port)) },
        ];
      },
    },
  ],
});

In order to pass sensitive information to the container, use secrets instead of environment. AWS will fetch values from Secret Manager based on arn that is provided for the valueFrom field.

const project = new studion.Project('demo-project', {
  services: [
    {
      type: 'WEB_SERVER',
      serviceName: 'api',
      image: imageUri,
      port: 3000,
      domain: 'api.my-domain.com',
      hostedZoneId: 'my-domain.com-hostedZoneId',
      secrets: [
        { name: 'DB_PASSWORD', valueFrom: 'arn-of-the-secret-manager-secret' },
      ],
    },
  ],
});
const project = new studion.Project('demo-project', {
  services: [
    {
      type: 'REDIS',
      serviceName: 'redis',
      dbName: 'test-db',
    },
    {
      type: 'WEB_SERVER',
      serviceName: 'api',
      image: imageUri,
      port: 3000,
      domain: 'api.my-domain.com',
      hostedZoneId: 'my-domain.com-hostedZoneId',
      secrets: (services: Services) => {
        const redisServiceName = 'redis';
        const redis = services[redisServiceName];
        return [
          { name: 'REDIS_PASSWORD', valueFrom: redis.passwordSecret.arn },
        ];
      },
    },
  ],
});

Database

AWS RDS Postgres instance.

Features:

  • enabled encryption with a symmetric encryption key
  • deployed inside an isolated subnet
  • backup enabled with retention period set to 14 days
new Database(name: string, args: DatabaseArgs, opts?: pulumi.CustomResourceOptions);

| Argument | Description | | :------- | :--------------------------------------------: | | name * | The unique name of the resource. | | args * | The arguments to resource properties. | | opts | Bag of options to control resource's behavior. |

type DatabaseArgs = {
  dbName: pulumi.Input<string>;
  username: pulumi.Input<string>;
  vpcId: pulumi.Input<string>;
  isolatedSubnetIds: pulumi.Input<pulumi.Input<string>[]>;
  vpcCidrBlock: pulumi.Input<string>;
  password?: pulumi.Input<string>;
  multiAz?: pulumi.Input<boolean>;
  applyImmediately?: pulumi.Input<boolean>;
  skipFinalSnapshot?: pulumi.Input<boolean>;
  allocatedStorage?: pulumi.Input<number>;
  maxAllocatedStorage?: pulumi.Input<number>;
  instanceClass?: pulumi.Input<string>;
  enableMonitoring?: pulumi.Input<boolean>;
  parameterGroupName?: pulumi.Input<string>;
  tags?: pulumi.Input<{
    [key: string]: pulumi.Input<string>;
  }>;
};

If the password is not specified, it will be autogenerated. The database password is stored as a secret inside AWS Secret Manager. The secret will be available on the Database resource as password.secret.

Database Replica

AWS RDS Postgres instance.

Features:

  • enabled encryption with a symmetric encryption key
  • deployed inside an isolated subnet
new DatabaseReplica(name: string, args: DatabaseReplicaArgs, opts?: pulumi.CustomResourceOptions);

| Argument | Description | | :------- | :--------------------------------------------: | | name * | The unique name of the resource. | | args * | The arguments to resource properties. | | opts | Bag of options to control resource's behavior. |

type DatabaseReplicaArgs = {
  replicateSourceDb: pulumi.Input<string>;
  dbSecurityGroupId: pulumi.Input<string>;
  dbSubnetGroupName?: pulumi.Input<string>;
  monitoringRole?: aws.iam.Role;
  multiAz?: pulumi.Input<boolean>;
  applyImmediately?: pulumi.Input<boolean>;
  allocatedStorage?: pulumi.Input<number>;
  maxAllocatedStorage?: pulumi.Input<number>;
  instanceClass?: pulumi.Input<string>;
  parameterGroupName?: pulumi.Input<string>;
  tags?: pulumi.Input<{
    [key: string]: pulumi.Input<string>;
  }>;
};

Database replica requires primary DB instance to exist. If the replica is in the same region as primary instance, we should not set dbSubnetGroupNameParam. The replicateSourceDb param is referenced like this:

  const primaryDb = new studion.Database(...);
  const replica = new studion.DatabaseReplica('replica', {
    replicateSourceDb: primaryDb.instance.identifier
  });

Redis

Upstash Redis instance.

Prerequisites

  1. Stack Config

| Name | Description | Secret | | :---------------- | :-----------------: | :----: | | upstash:email * | Upstash user email. | true | | upstash:apiKey * | Upstash API key. | true |

$ pulumi config set --secret upstash:email [email protected]
$ pulumi config set --secret upstash:apiKey my-api-key
new Redis(name: string, args: RedisArgs, opts: RedisOptions);

| Argument | Description | | :------- | :--------------------------------------------: | | name * | The unique name of the resource. | | args * | The arguments to resource properties. | | opts | Bag of options to control resource's behavior. |

type RedisArgs = {
  dbName: pulumi.Input<string>;
  region?: pulumi.Input<string>;
};

interface RedisOptions extends pulumi.ComponentResourceOptions {
  provider: upstash.Provider;
}

After creating the Redis resource, the passwordSecret AWS Secret Manager Secret will exist on the resource.

Static Site

AWS S3 + Cloudfront.

Features:

  • creates TLS certificate for the specified domain
  • redirects HTTP traffic to HTTPS
  • enables http2 and http3 protocols
  • uses North America and Europe edge locations
new StaticSite(name: string, args: StaticSiteArgs, opts?: pulumi.ComponentResourceOptions );

| Argument | Description | | :------- | :--------------------------------------------: | | name * | The unique name of the resource. | | args * | The arguments to resource properties. | | opts | Bag of options to control resource's behavior. |

type StaticSiteArgs = {
  domain?: pulumi.Input<string>;
  hostedZoneId?: pulumi.Input<string>;
  tags?: pulumi.Input<{
    [key: string]: pulumi.Input<string>;
  }>;
};

Web Server

AWS ECS Fargate.

Features:

  • memory and CPU autoscaling enabled
  • creates TLS certificate for the specified domain
  • redirects HTTP traffic to HTTPS
  • creates CloudWatch log group
  • comes with predefined CPU and memory options: small, medium, large, xlarge
new WebServer(name: string, args: WebServerArgs, opts?: pulumi.ComponentResourceOptions );

| Argument | Description | | :------- | :--------------------------------------------: | | name * | The unique name of the resource. | | args * | The arguments to resource properties. | | opts | Bag of options to control resource's behavior. |

export type WebServerArgs = {
  image: pulumi.Input<string>;
  port: pulumi.Input<number>;
  clusterId: pulumi.Input<string>;
  clusterName: pulumi.Input<string>;
  vpcId: pulumi.Input<string>;
  vpcCidrBlock: pulumi.Input<string>;
  publicSubnetIds: pulumi.Input<pulumi.Input<string>[]>;
  domain?: pulumi.Input<string>;
  hostedZoneId?: pulumi.Input<string>;
  desiredCount?: pulumi.Input<number>;
  autoscaling?: pulumi.Input<{
    enabled: pulumi.Input<boolean>;
    minCount?: pulumi.Input<number>;
    maxCount?: pulumi.Input<number>;
  }>;
  size?: pulumi.Input<Size>;
  environment?: aws.ecs.KeyValuePair[];
  secrets?: aws.ecs.Secret[];
  healthCheckPath?: pulumi.Input<string>;
  taskExecutionRoleInlinePolicies?: pulumi.Input<
    pulumi.Input<RoleInlinePolicy>[]
  >;
  taskRoleInlinePolicies?: pulumi.Input<pulumi.Input<RoleInlinePolicy>[]>;
  tags?: pulumi.Input<{
    [key: string]: pulumi.Input<string>;
  }>;
};

Nuxt SSR preset

AWS ECS Fargate + Cloudfront.

Features:

  • memory and CPU autoscaling enabled
  • creates TLS certificate for the specified domain
  • redirects HTTP traffic to HTTPS
  • creates CloudWatch log group
  • comes with predefined CPU and memory options: small, medium, large, xlarge
  • CDN in front of the application load balancer for static resource caching
new NuxtSSR(name: string, args: NuxtSSRArgs, opts?: pulumi.ComponentResourceOptions );

| Argument | Description | | :------- | :--------------------------------------------: | | name * | The unique name of the resource. | | args * | The arguments to resource properties. | | opts | Bag of options to control resource's behavior. |

export type NuxtSSRArgs = {
  image: pulumi.Input<string>;
  port: pulumi.Input<number>;
  clusterId: pulumi.Input<string>;
  clusterName: pulumi.Input<string>;
  vpcId: pulumi.Input<string>;
  vpcCidrBlock: pulumi.Input<string>;
  publicSubnetIds: pulumi.Input<pulumi.Input<string>[]>;
  domain?: pulumi.Input<string>;
  hostedZoneId?: pulumi.Input<string>;
  desiredCount?: pulumi.Input<number>;
  autoscaling?: pulumi.Input<{
    enabled: pulumi.Input<boolean>;
    minCount?: pulumi.Input<number>;
    maxCount?: pulumi.Input<number>;
  }>;
  size?: pulumi.Input<Size>;
  environment?: aws.ecs.KeyValuePair[];
  secrets?: aws.ecs.Secret[];
  healthCheckPath?: pulumi.Input<string>;
  tags?: pulumi.Input<{
    [key: string]: pulumi.Input<string>;
  }>;
};

Mongo

AWS ECS Fargate.

Features:

  • persistent storage
  • service auto-discovery
  • creates CloudWatch log group
  • comes with predefined CPU and memory options: small, medium, large, xlarge
new Mongo(name: string, args: MongoArgs, opts?: pulumi.ComponentResourceOptions );

| Argument | Description | | :------- | :--------------------------------------------: | | name * | The unique name of the resource. | | args * | The arguments to resource properties. | | opts | Bag of options to control resource's behavior. |

export type MongoArgs = {
  clusterId: pulumi.Input<string>;
  clusterName: pulumi.Input<string>;
  vpcId: pulumi.Input<string>;
  vpcCidrBlock: pulumi.Input<string>;
  privateSubnetIds: pulumi.Input<pulumi.Input<string>[]>;
  username: pulumi.Input<string>;
  password?: pulumi.Input<string>;
  port?: pulumi.Input<number>;
  size?: pulumi.Input<Size>;
  tags?: pulumi.Input<{
    [key: string]: pulumi.Input<string>;
  }>;
};

If the password is not specified it will be autogenerated. The Mongo password is stored as a secret inside AWS Secret Manager. The secret will be available on the Mongo resource as password.secret.

Ecs Service

AWS ECS Fargate.

Features:

  • memory and CPU autoscaling
  • service auto-discovery
  • persistent storage
  • CloudWatch logs
  • comes with predefined cpu and memory options: small, medium, large, xlarge
new EcsService(name: string, args: EcsServiceArgs, opts?: pulumi.ComponentResourceOptions );

| Argument | Description | | :------- | :--------------------------------------------: | | name * | The unique name of the resource. | | args * | The arguments to resource properties. | | opts | Bag of options to control resource's behavior. |

export type EcsServiceArgs = {
  image: pulumi.Input<string>;
  port: pulumi.Input<number>;
  clusterId: pulumi.Input<string>;
  clusterName: pulumi.Input<string>;
  vpcId: pulumi.Input<string>;
  vpcCidrBlock: pulumi.Input<string>;
  subnetIds: pulumi.Input<pulumi.Input<string>[]>;
  desiredCount?: pulumi.Input<number>;
  autoscaling?: pulumi.Input<{
    enabled: pulumi.Input<boolean>;
    minCount?: pulumi.Input<number>;
    maxCount?: pulumi.Input<number>;
  }>;
  size?: pulumi.Input<Size>;
  environment?: aws.ecs.KeyValuePair[];
  secrets?: aws.ecs.Secret[];
  enableServiceAutoDiscovery: pulumi.Input<boolean>;
  persistentStorageVolumePath?: pulumi.Input<string>;
  dockerCommand?: pulumi.Input<string[]>;
  lbTargetGroupArn?: aws.lb.TargetGroup['arn'];
  securityGroup?: aws.ec2.SecurityGroup;
  assignPublicIp?: pulumi.Input<boolean>;
  taskExecutionRoleInlinePolicies?: pulumi.Input<
    pulumi.Input<RoleInlinePolicy>[]
  >;
  taskRoleInlinePolicies?: pulumi.Input<pulumi.Input<RoleInlinePolicy>[]>;
  tags?: pulumi.Input<{
    [key: string]: pulumi.Input<string>;
  }>;
};

Exec into running ECS task

Prerequisites

  1. Install the Session Manager plugin
$ brew install --cask session-manager-plugin
  1. Install jq
$ brew install jq

In order to exec into running ECS container run the following command:

aws ecs execute-command  \
  --cluster CLUSTER_NAME \
  --task $(aws ecs list-tasks --cluster CLUSTER_NAME --family TASK_FAMILY_NAME | jq -r '.taskArns[0] | split("/")[2]') \
  --command "/bin/sh" \
  --interactive

Where CLUSTER_NAME is the name of the ECS cluster and TASK_FAMILY_NAME is the name of the task family that task belongs to.

SSM Connect

The Database component deploys a database instance inside an isolated subnet, and it's not publicly accessible from outside of VPC. In order to connect to the database we need to deploy the ec2 instance which will be used to forward traffic to the database instance. Because of security reasons, the ec2 instance is deployed inside a private subnet which means we can't directly connect to it. For that purpose, we use AWS System Manager which enables us to connect to the ec2 instance even though it's inside a private subnet. Another benefit of using AWS SSM is that we don't need a ssh key pair.

AWS RDS connection schema

Prerequisites

  1. Install the Session Manager plugin
$ brew install --cask session-manager-plugin

SSM Connect can be enabled by setting enableSSMConnect property to true.

const project = new studion.Project('demo-project', {
  enableSSMConnect: true,
  ...
});

export const ec2InstanceId = project.ec2SSMConnect?.ec2.id;

Open up your terminal and run the following command:

$ aws ssm start-session --target EC2_INSTANCE_ID --document-name AWS-StartPortForwardingSessionToRemoteHost --parameters '{"host": ["DATABASE_ADDRESS"], "portNumber":["DATABASE_PORT"], "localPortNumber":["5555"]}'

Where EC2_INSTANCE_ID is an ID of the EC2 instance that is created for you (ID can be obtained by exporting it from the stack), and DATABASE_ADDRESS and DATABASE_PORT are the address and port of the database instance.

And that is it! 🥳 Now you can use your favorite database client to connect to the database.

RDS connection

It is important that for the host you set localhost and for the port you set 5555 because we are port-forwarding traffic from localhost:5555 to DATABASE_ADDRESS:DATABASE_PORT. For the user, password, and database field, set values which are set in the Project.

const project = new studion.Project('demo-project', {
  enableSSMConnect: true,
  services: [
    {
      type: 'DATABASE',
      dbName: 'database_name',
      username: 'username',
      password: 'password',
      ...
    }
  ]
});

🚧 TODO

  • [ ] Add worker service for executing tasks
  • [ ] Enable RDS password rotation