Code Monkey home page Code Monkey logo

async-labs / saas Goto Github PK

View Code? Open in Web Editor NEW
3.9K 95.0 663.0 6.55 MB

Build your own SaaS business with SaaS boilerplate. Productive stack: React, Material-UI, Next, MobX, WebSockets, Express, Node, Mongoose, MongoDB. Written with TypeScript.

Home Page: https://saas-app.async-await.com

License: MIT License

JavaScript 1.57% TypeScript 98.19% CSS 0.23% Shell 0.01%
saas boilerplate react material-ui nextjs mobx express mongoose mongodb typescript

saas's Introduction

image

Support Ukraine: link

SaaS Boilerplate

Open source web app that saves you many days of work when building your own SaaS product. The boilerplate comes with many basic SaaS features (see Features below) so that you can focus on features that differentiate your product.

If you want to learn how to build this project from scratch, check out our book: https://builderbook.org/book

The open source project is located in the saas folder. If you purchased our book, codebases for each of the book's chapters are located in the book folder.

We've used this saas project to build:

  • Builder Book - learn how to build full-stack web apps from scratch
  • SaaS Boilerplate - open source web app to build your own SaaS product
  • Work in biotech - job board for biotech startup companies
  • AI-cruiter - browser extension is built for recruiters managing a high volume of job applicants. AI-cruiter uses LLMs - like ChatGPT and PaLM 2 - to generate succinct and relevant summaries of your job applicants' resumes
  • Async - open source urgent vs non-urgent team communication tool for small teams
  • Async Labs - many custom dev projects

Live demo:

Sponsors

aws-activate-logo

1password-logo

Showcase

Check out projects built with the help of this open source app. Feel free to add your own project by creating a pull request.

  • Async: Open source web app for team communication, separate urgent vs. non-urgent conversations.
  • workinbiotech.com: Work in biotech, job board for small and young biotech companies
  • Retaino by Earl Lee: Save, annotate, review, and share great web content. Receive smart email digests to retain key information.
  • Builder Book: Open source web app to publish documentation or books.

Contents

Features

  • Server-side rendering for fast initial load and SEO.
  • User authentication with Google OAuth API and Passwordless, cookie, and session.
  • Production-ready Express server with compression, parser, and helmet.
  • Transactional emails (AWS SES): welcome, team invitation, and payment.
  • Adding email addresses to newsletter lists (Mailchimp): new users, paying users.
  • File upload, load, and deletion (AWS S3) with pre-signed request for: Posts, Team Profile, and User Profile.
  • Websockets with socket.io v3.
  • Team creation, Team Member invitation, and settings for Team and User.
  • Opinionated architecture:
    • keeping babel and webpack configurations under the hood,
    • striving to minimize number of configurations,
    • withAuth HOC to pass user prop and control user access to pages,
    • HOC extensions MyApp and MyDocument
    • server-side rendering with Material-UI,
    • model-specific components in addition to common components.
  • Universally-available environmental variables at runtime.
  • Custom logger (configure what not to print in production).
  • Useful components for any web app: ActiveLink, Confirm, Notifier, MenuWithLinks, and more.
  • Analytics with Google Analytics.
  • Production-ready, scalable architecture:
    • app - user-facing web app with Next/Express server, responsible for rendering pages (either client-side or server-side rendered). app sends requests via API methods to api Express server.
    • api - server-only code, Express server, responsible for processing requests for internal and external API infrastructures.
  • Subscriptions with Stripe:
    • subscribe/unsubscribe Team to plan,
    • update card information,
    • verified Stripe webhook for failed payment for subscription.

Running api locally:

  • Before running, create a .env file inside the api folder with the environmental variables as shown below. These variables are also listed in .env.example, which you can use as a template to create your own .env file inside the api foler.

api/.env:

# Used in api/server/server.ts
MONGO_URL_TEST=
MONGO_URL=
SESSION_NAME=
SESSION_SECRET=
COOKIE_DOMAIN=

# Used in api/server/google.ts
GOOGLE_CLIENTID=
GOOGLE_CLIENTSECRET=

# Used in api/server/aws-s3.ts and api/server/aws-ses.ts
AWS_REGION=
AWS_ACCESSKEYID=
AWS_SECRETACCESSKEY=

# Used in api/server/models/Invitation.ts and api/server/models/User.ts
EMAIL_SUPPORT_FROM_ADDRESS=

# Used in api/server/mailchimp.ts
MAILCHIMP_API_KEY=
MAILCHIMP_REGION=
MAILCHIMP_SAAS_ALL_LIST_ID=

----------
# All env variables above this line are needed for successful user signup

# Used in api/server/stripe.ts
STRIPE_TEST_SECRETKEY=sk_test_xxxxxx
STRIPE_LIVE_SECRETKEY=sk_live_xxxxxx

STRIPE_TEST_PLANID=plan_xxxxxx
STRIPE_LIVE_PLANID=plan_xxxxxx

STRIPE_LIVE_ENDPOINTSECRET=whsec_xxxxxx

# Optionally determine the URL
URL_APP="http://localhost:3000"
URL_API="http://localhost:8000"
PRODUCTION_URL_APP="https://saas-app.async-await.com"
PRODUCTION_URL_API="https://saas-api.async-await.com"
  • Your .env file file must have values for the required variables. To use all features and third-party integrations, also add the optional variables.

  • IMPORTANT: do not publish your actual values for environmentable variables in .env.example; this file is public and only meant to show you how your .env should look.

  • IMPORTANT: use your values for PRODUCTION_URL_APP and PRODUCTION_URL_API. These are values for domain name that you own.

  • IMPORTANT: The above environmental variables are available on the server only. You should add your .env file to .gitignore inside the api folder so that your secret keys are not stored on a remote Github repo.

  • To get value for MONGO_URL_TEST, we recommend you use a free MongoDB at MongoDB Atlas or $15/month MongoDB at Digital Ocean

  • Specify your own name and secret keys for Express session: SESSION_NAME and SESSION_SECRET

  • Get GOOGLE_CLIENTID and GOOGLE_CLIENTSECRET by following the official OAuth tutorial.
    Important: For Google OAuth app, callback URL is: http://localhost:8000/oauth2callback
    Important: You have to enable Google+ API in your Google Cloud Platform account.

  • Once .env is created, you can run the api app. Navigate to the api folder, run yarn install to add all packages, then run the command below:

    yarn dev
    

Running app locally:

  • Navigate to the app folder, run yarn to add all packages, then run yarn dev and navigate to http://localhost:3000:

    • A .env file in the app folder is not required to run, but you can create one to override the default variables. The environmental variables for .env in the app folder are shown below. You can also refer .env.example for creating your own .env file in the app folder.
      NEXT_PUBLIC_STRIPE_TEST_PUBLISHABLEKEY="pk_test_xxxxxxxxxxxxxxx"
      NEXT_PUBLIC_STRIPE_LIVE_PUBLISHABLEKEY="pk_live_xxxxxxxxxxxxxxx"
      
      NEXT_PUBLIC_BUCKET_FOR_POSTS=
      NEXT_PUBLIC_BUCKET_FOR_TEAM_AVATARS=
      NEXT_PUBLIC_BUCKET_FOR_TEAM_LOGOS=
      
      NEXT_PUBLIC_URL_APP="http://localhost:3000"
      NEXT_PUBLIC_URL_API="http://localhost:8000"
      NEXT_PUBLIC_PRODUCTION_URL_APP=
      NEXT_PUBLIC_PRODUCTION_URL_API=
      
      NEXT_PUBLIC_API_GATEWAY_ENDPOINT=
      NEXT_PUBLIC_GA_MEASUREMENT_ID=
    
    • IMPORTANT: do not publish your actual values for environmentable variables in .env.example; this file is public and only meant to show you how your .env should look.

    • IMPORTANT: use your values for PRODUCTION_URL_APP and PRODUCTION_URL_API. These are values for domain name that you own.

    • To get NEXT_PUBLIC_GA_MEASUREMENT_ID, set up Google Analytics and follow these instructions to find your tracking ID.

    • To get NEXT_PUBLIC_STRIPE_TEST_PUBLISHABLEKEY, go to your Stripe dashboard, click Developers, then click API keys.

  • For successful file uploading, make sure your buckets have proper CORS configuration. Go to your AWS account, find your bucket, go to Permissions > CORS configuration, add:

[
  {
    "AllowedHeaders":[
      "*"
    ],
    "AllowedMethods":[
      "PUT",
      "POST",
      "GET",
      "HEAD",
      "DELETE"
    ],
    "AllowedOrigins":[
      "http://localhost:3000",
      "https://saas-app.async-await.com"
    ],
    "ExposeHeaders":[
      "ETag",
      "x-amz-meta-custom-header"
    ]
  }
]
  • Make sure to update allowed origin with your actual values for NEXT_PUBLIC_URL_APP and NEXT_PUBLIC_PRODUCTION_URL_APP.

  • Once .env is created, you can run the app app. Navigate to the app folder, run yarn install to add all packages, then run the command below:

    yarn dev
    

Symlink api in lambda:

In lambda directory we are symlinking api directory. You can run symlink command in lambda folder as mentioned below:

bash symlink ../api

Deploy to Heroku, AWS Elastic Beanstalk, API Gateway and AWS Lambda

We give detailed instructions inside Chapter 9 and 10 of our SaaS Boilerplate book: https://builderbook.org/book

Built with

For more detail, check package.json files in both app and api folders and project's root.

To customize styles, check this guide.

Screenshots

Google or passwordless login: 1_SaaS_login

Dropdown menu for settings: 2_SaaS_DropdownMenu

Personal settings: 3_SaaS_PersonalSettings

Team settings: 4_SaaS_TeamSettings

Creating a Discussion: 5_SaaS_Discussion_Creation

Writing a Post, Markdown vs. HTML view: 6_SaaS_Discussion_Markdown

7_SaaS_Discussion_HTML

Discussion between team members: 8_SaaS_Discussion_Dark

Billing settings: 9_SaaS_Billing

Purchasing a subscription: 10_SaaS_BuySubscription

Payment history: 12_SaaS_PaymentHistory

Contributing

Want to support this project? Consider buying our books.

Team

You can contact us at [email protected].

If you are interested in working with us, check out Async Labs.

License

All code in this repository is provided under the MIT License.

Project structure

├── .elasticbeanstalk
│   └── config.yml
├── .github
│   └── FUNDING.yml
├── .vscode
│   ├── extensions.json
│   ├── launch.json
│   └── settings.json
├── api
│   ├── .elasticbeanstalk
│   │   └── config.yml
│   ├── server
│   │   ├── api
│   │   │   ├── index.ts
│   │   │   ├── public.ts
│   │   │   ├── team-leader.ts
│   │   │   └── team-member.ts
│   │   ├── models
│   │   │   ├── Discussion.ts
│   │   │   ├── EmailTemplate.ts
│   │   │   ├── Invitation.ts
│   │   │   ├── Post.ts
│   │   │   ├── Team.ts
│   │   │   └── User.ts
│   │   ├── utils
│   │   │   ├── slugify.ts
│   │   │   └── sum.ts
│   │   ├── aws-s3.ts
│   │   ├── aws-ses.ts
│   │   ├── google-auth.ts
│   │   ├── logger.ts
│   │   ├── mailchimp.ts
│   │   ├── passwordless-auth.ts
│   │   ├── passwordless-token-mongostore.ts
│   │   ├── server.ts
│   │   ├── sockets.ts
│   │   └── stripe.ts
│   ├── static
│   │   └── robots.txt
│   ├── test/server/utils
│   │   ├── slugify.test.ts
│   │   └── sum.test.ts
│   ├── .eslintignore
│   ├── .eslintrc.js
│   ├── .gitignore
│   ├── package.json
│   ├── tsconfig.json
│   ├── tsconfig.server.json
│   └── yarn.lock
├── app
│   ├── .elasticbeanstalk
│   │   └── config.yml
│   ├── components
│   │   ├── common
│   │   │   ├── Confirmer.tsx
│   │   │   ├── LoginButton.tsx
│   │   │   ├── MemberChooser.tsx
│   │   │   ├── MenuWithLinks.tsx
│   │   │   ├── MenuWithMenuItems.tsx
│   │   │   └── Notifier.tsx
│   │   ├── discussions
│   │   │   ├── CreateDiscussionForm.tsx
│   │   │   ├── DiscussionActionMenu.tsx
│   │   │   ├── DiscussionList.tsx
│   │   │   ├── DiscussionListItem.tsx
│   │   │   └── EditDiscussionForm.tsx
│   │   ├── layout
│   │   │   ├── index.tsx
│   │   ├── posts
│   │   │   ├── PostContent.tsx
│   │   │   ├── PostDetail.tsx
│   │   │   ├── PostEditor.tsx
│   │   │   └── PostForm.tsx
│   │   ├── teams
│   │   │   └── InviteMember.tsx
│   ├── lib
│   │   ├── api
│   │   │   ├── makeQueryString.ts
│   │   │   ├── public.ts
│   │   │   ├── sendRequestAndGetResponse.ts
│   │   │   ├── team-leader.ts
│   │   │   └── team-member.ts
│   │   ├── store
│   │   │   ├── discussion.ts
│   │   │   ├── index.ts
│   │   │   ├── invitation.ts
│   │   │   ├── post.ts
│   │   │   ├── team.ts
│   │   │   └── user.ts
│   │   ├── confirm.ts
│   │   ├── isMobile.ts
│   │   ├── notify.ts
│   │   ├── resizeImage.ts
│   │   ├── sharedStyles.ts
│   │   ├── theme.ts
│   │   └── withAuth.tsx
│   ├── pages
│   │   ├── _app.tsx
│   │   ├── _document.tsx
│   │   ├── billing.tsx
│   │   ├── create-team.tsx
│   │   ├── discussion.tsx
│   │   ├── invitation.tsx
│   │   ├── login-cached.tsx
│   │   ├── login.tsx
│   │   ├── team-settings.tsx
│   │   └── your-settings.tsx
│   ├── public
│   │   └── pepe.jpg
│   ├── server
│   │   ├── robots.txt
│   │   ├── routesWithCache.ts
│   │   ├── server.ts
│   │   └── setupSitemapAndRobots.ts
│   ├── .babelrc
│   ├── .eslintignore
│   ├── .eslintrc.js
│   ├── .gitignore
│   ├── next.env.d.ts
│   ├── next.config.js
│   ├── package.json
│   ├── tsconfig.json
│   ├── tsconfig.server.json
│   └── yarn.lock
├── book
├── lambda
│   ├── .estlintignore
│   ├── .eslintrc.js
│   ├── .gitignore
│   ├── api
│   ├── handler.ts
│   ├── package.json
│   ├── serverless.yml
│   ├── tsconfig.json
│   └── yarn.lock
├── .gitignore
├── LICENSE.md
├── README.md
├── package.json
├── yarn.lock

saas's People

Contributors

athahersirnaik avatar aviggiano avatar batamar avatar delgermurun avatar flourish90 avatar kaveet avatar klyburke avatar pbaveja avatar tima101 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

saas's Issues

mobile UI

Grids work as expected but we should add better spacing and hide some elements on mobile browser.


Use <Hidden smDown> from Material-UI to hide some elements on mobile browser.


Send user-agent to server so server has isMobile and renders appropriate styles.

const mobileRE = /(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows (ce|phone)|xda|xiino/i;

function isMobile(opts) {
  if (!opts) {
    opts = {};
  }

  let ua = opts.ua;
  if (!ua && typeof navigator !== 'undefined') {
    ua = navigator.userAgent;
  }
  if (!ua && opts.req && opts.req.headers && typeof opts.req.headers['user-agent'] === 'string') {
    ua = opts.req.headers['user-agent'];
  }

  if (typeof ua !== 'string') {
    return false;
  }

  return mobileRE.test(ua);
}

User cannot delete their account and data

Hi all,

Great demo, love what you're doing. I have an issue around compliance. I have built something similar a few times and I hope by raising this now it saves you a headache down the line.

I am unable to manage user data inline with GDPR. Typically this equates to:

  • Delete user
  • Request all data linked to user
  • [optional] Obfuscation on linked items. I.E. If a user is deleted but they posted an image, and this image is owned by the company not the user, the username/id link is obfuscated when the user deletes their account. This allows the image to exist and the user to delete their account. This is pretty tedious and not recommended unless deleting a user breaks areas of the application.

I know it is early stages but I would recommend adding in an "accept EULA" model which the user can see in the account area. And allow GA tracking to be optional:

  • EULA and privacy policy is mission
  • GA tracking being optional

I see the above as a requirement for a B2B template.

Best,

Can't start API (.env, MONGO_URL)

I've configured .env as instructed, hitting an error when trying to start the API locally:

(node:22825) UnhandledPromiseRejectionWarning: TypeError: First argument must be a string, Buffer, ArrayBuffer, Array, or array-like object.
at Function.Buffer.from (buffer.js:202:9)
at new Buffer (buffer.js:158:17)

Tasks before v1

Tasks before v1:

  • README
  • TL: Settings page
  • TM: Settings page
  • improve team invitation flow
  • improve file upload to S3
  • add email address to Mailchimp list
  • split Settings: Team Settings and Personal Settings
  • Stripe subscription
  • create customer with source (default payment method)
  • create subscription using customer and plan
  • retrieve card object, update customer's default_source (add/edit card information)
  • Unsubscribe internal API
  • add UI to TeamBilling and YourSettings pages
  • webhook for failed subscription payment
  • update README to explain how subscription works (product, plan, customer, default source, subscription)
  • redeploy demo,
  • release version 1

@klyburke @delgermurun I plan to add Stripe subscriptions later this week, then we will release v1 !


Decided against admin and homepage apps.


Done:

  • Initial boilerplate and project structure
  • Add TypeScript
  • Add MobX
  • Create Team (by TL)
  • Invite TM
  • Google OAuth
  • AWS SES
  • Mailchimp API

Exposed mongodb credentials

Potential security breach. Verified credentials in URL *redacted*:*redacted*@dedicated-dev-m10-us-west-1-shard-00-00-eoobe.mongodb.net in file /book/4-begin/api/.note

You should recycle your credentials immediately and move secrets to environment variables to prevent them being committed to GitHub.

Paid plan

  • Limit number of team members inside team for free plan to 2,
  • Show notifier with text and link that leads to /billing page
  • In order to add a third team member, team leader has to upgrade to the paid plan.

This issue is part of #14





Exposed mongodb credentials

Potential security breach. Verified credentials in URL *redacted*:*redacted*@dedicated-dev-m10-us-west-1-shard-00-00-eoobe.mongodb.net in file /book/2-begin/api/.note

You should recycle your credentials immediately and move secrets to environment variables to prevent them being committed to GitHub.

Cannot get past the /login screen

Hello,

First of all, thank you a lot for this very promising boilerplate. I was just searching for something similar when I came across it on Hacker News.

I have tried to run it locally.

I did setup so far two MongoDB instances, Express session, Google oAuth secrets, and Mailchimp.

Upon trying to login, I am indeed redirected to the google login page. When I do login with Google, I am sent back to /create-team which immediately loads /login. Accessing /create-team directly also redirects me to /login. The user is however sent to my mailchimp list indeed.

Am I missing something obvious / doing something wrong?
Should I setup all the other providers (such as Amazon) for saas to work?

Thank you for your time.

Exposed mongodb credentials

Potential security breach. Verified credentials in URL *redacted*:*redacted*@dedicated-dev-m10-us-west-1-shard-00-00-eoobe.mongodb.net in file /book/3-begin/api/.note

You should recycle your credentials immediately and move secrets to environment variables to prevent them being committed to GitHub.

Information About Purchase to Builder Book/Saas

First of all, congratulations, I have a question; How do I know if your payment method is valid at my location? Because I've had trouble with that before. I'm waiting for your answer.

Thanks in advance.

I live in Istanbul/Turkey.

Create more complete guide for acquiring .env values

I'm not sure if this will be included when the book is finished, but getting the config values for the .env file requires getting values from a lot of different sources and different configurations. It would be helpful if there was better documentation around that process.

I would love to submit a PR for it, but I haven't been able to figure all of it out myself yet.

Yarn warnings

I have just started going through the book. I am in 1-begin and running yarn for the very first time after creating package.json and I get the following warnings:

yarn install v1.19.2
info No lockfile found.
[1/4] 🔍  Resolving packages...
warning next > @babel/runtime-corejs2 > [email protected]: core-js@<3.0 is no longer maintained and not recommended for usage due to the number of issues. Please, upgrade your dependencies to the actual version of core-js@3.
warning next > fork-ts-checker-webpack-plugin > chokidar > [email protected]: One of your dependencies needs to upgrade to fsevents v2: 1) Proper nodejs v10+ support 2) No more fetching binaries from AWS, smaller package size
warning next > styled-jsx > babel-types > babel-runtime > [email protected]: core-js@<3.0 is no longer maintained and not recommended for usage due to the number of issues. Please, upgrade your dependencies to the actual version of core-js@3.

while these are just warnings I don't anticipate this causing a problem but would like to see a clean output.

node version: 13.2.0
development environment: OSX Mojave 10.14.6

Main todo list

@klyburke @delgermurun Below is the list of improvements I had in mind, if you have time, please suggest more.

  • make main layout, settings, billing similar to Async so it is easier to maintain
  • UX/UI improvements to Posts (similar to what we did for Async)
  • toggle for dark/light theme
  • AWS Lambda and SES: email notification for new Post (new or existing Discussion)
  • limit number of team members inside team for free plan to 2, show notifier that asks to become a paid customer when team leader adds a third member
  • websokets see #23
  • passwordless authentication in addition to Google OAuth
  • write an introduction article at: https://async-await.com/articles
Click to see Hill for issue #14
Single issue hill
Click to see Hill for all issues
All issue hill

created by [Async](https://async-await.com/

UX/UI refactor

This is a first task from #14

  • Main Menu (MM)
  • All pages with settings
  • Discussion forms (create and edit)
  • Discussion page (embed new Post form)
  • list of Discussions
  • UI for mobile browser (`isMobile)

code base structure and server code

I find it very common in the Node community write code this way. There is nothing wrong with it, however, in the last few years I have moved away from it and started using clean architecture.

In the example below the code base uses OOP - you can however easily replace it with functional paradigm. Try to ignore the paradigm used, look at the structure which i think works well. The drawback is a bit more boiler plate code though.

This is an example:
https://github.com/jaylensoeur/momenton-task

So my question is:
Is this too complicated or overkill? and what is your opinion on the example I've linked.

Thanks

darkTheme

  • darkTheme and lightTheme for entire app
  • save darkTheme boolean parameter to User model on the server and store.currentUser.darkTheme on the client

This issue is part of #14



Rest API

Hi there,
I am wandering if the rest API is safe to expose publicly, documenting it in OpenAPI format and extending it with application specific code?

Has anybody done this?

Exposed mongodb credentials

Potential security breach. Verified credentials in URL *redacted*:*redacted*@dedicated-dev-m10-us-west-1-shard-00-00-eoobe.mongodb.net in file /book/3-end/api/.note

You should recycle your credentials immediately and move secrets to environment variables to prevent them being committed to GitHub.

UserNotFound: Could not find user root@admin

Followed the instructions and brought up the docker instances (Win10). It looks like the database isn't automatically creating the admin user (?) and I'm not sure how to manually create that user in the db, or whether I should. I have tried resetting, cleaning the directories. I have copy-pasted the .env file for local testing, but am getting that error. Going to http://app.saas.localhost:3000 also has no effect. Should I have configured something manually? I THINK I did everything right, but I can't get it to run. Log below.

yarn run v1.13.0
$ docker-compose up
Creating network "app_default" with driver "bridge"
Creating saas-mongo ... done
Creating saas-mongoexp ... done
Creating saas-api ... done
Creating saas-app ... done
Attaching to saas-mongo, saas-mongoexp, saas-api, saas-app
saas-mongoexp | Waiting for saas-mongo:27017...
saas-mongo | 2019-08-06T19:27:03.508+0000 I CONTROL [main] Automatically disabling TLS 1.0, to force-enable TLS 1.0 specify --sslDisabledProtocols 'none'
saas-mongo | 2019-08-06T19:27:03.513+0000 I CONTROL [initandlisten] MongoDB starting : pid=1 port=27017 dbpath=/data/db 64-bit host=6312e08b6a63
saas-mongo | 2019-08-06T19:27:03.513+0000 I CONTROL [initandlisten] db version v4.0.11
saas-mongoexp | /docker-entrypoint.sh: connect: Connection refused
saas-mongo | 2019-08-06T19:27:03.513+0000 I CONTROL [initandlisten] git version: 417d1a712e9f040d54beca8e4943edce218e9a8c
saas-mongo | 2019-08-06T19:27:03.513+0000 I CONTROL [initandlisten] OpenSSL version: OpenSSL 1.0.2g 1 Mar 2016
saas-mongo | 2019-08-06T19:27:03.513+0000 I CONTROL [initandlisten] allocator: tcmalloc
saas-mongo | 2019-08-06T19:27:03.513+0000 I CONTROL [initandlisten] modules: none
saas-mongo | 2019-08-06T19:27:03.513+0000 I CONTROL [initandlisten] build environment:
saas-mongo | 2019-08-06T19:27:03.514+0000 I CONTROL [initandlisten] distmod: ubuntu1604
saas-mongo | 2019-08-06T19:27:03.514+0000 I CONTROL [initandlisten] distarch: x86_64
saas-mongo | 2019-08-06T19:27:03.514+0000 I CONTROL [initandlisten] target_arch: x86_64
saas-mongo | 2019-08-06T19:27:03.514+0000 I CONTROL [initandlisten] options: { net: { bindIpAll: true }, security: { authorization: "enabled" } }
saas-mongo | 2019-08-06T19:27:03.515+0000 I STORAGE [initandlisten] Detected data files in /data/db created by the 'wiredTiger' storage engine, so setting the active storage engine to 'wiredTiger'.
saas-mongo | 2019-08-06T19:27:03.515+0000 I STORAGE [initandlisten]
saas-mongo | 2019-08-06T19:27:03.515+0000 I STORAGE [initandlisten] ** WARNING: Using the XFS filesystem is strongly recommended with the WiredTiger storage engine
saas-mongo | 2019-08-06T19:27:03.515+0000 I STORAGE [initandlisten] ** See http://dochub.mongodb.org/core/prodnotes-filesystem
saas-mongoexp | /docker-entrypoint.sh: line 14: /dev/tcp/saas-mongo/27017: Connection refused
saas-mongoexp | Tue Aug 6 19:27:05 UTC 2019 retrying to connect to saas-mongo:27017 (2/5)
saas-mongoexp | /docker-entrypoint.sh: connect: Connection refused
saas-app | yarn run v1.16.0
saas-mongo | 2019-08-06T19:27:03.515+0000 I STORAGE [initandlisten] wiredtiger_open config: create,cache_size=256M,session_max=20000,eviction=(threads_min=4,threads_max=4),config_base=false,statistics=(fast),log=(enabled=true,archive=true,path=journal,compressor=snappy),file_manager=(close_idle_time=100000),statistics_log=(wait=0),verbose=(recovery_progress),
saas-api | yarn run v1.16.0
saas-mongo | 2019-08-06T19:27:05.480+0000 I STORAGE [initandlisten] WiredTiger message [1565119625:480630][1:0x7fd98507ea80], txn-recover: Main recovery loop: starting at 2/8832 to 3/256
saas-mongo | 2019-08-06T19:27:05.729+0000 I STORAGE [initandlisten] WiredTiger message [1565119625:729153][1:0x7fd98507ea80], txn-recover: Recovering log 2 through 3
saas-mongo | 2019-08-06T19:27:05.886+0000 I STORAGE [initandlisten] WiredTiger message [1565119625:886212][1:0x7fd98507ea80], txn-recover: Recovering log 3 through 3
saas-mongo | 2019-08-06T19:27:06.014+0000 I STORAGE [initandlisten] WiredTiger message [1565119626:14941][1:0x7fd98507ea80], txn-recover: Set global recovery timestamp: 0
saas-mongoexp | /docker-entrypoint.sh: line 14: /dev/tcp/saas-mongo/27017: Connection refused
saas-mongoexp | Tue Aug 6 19:27:06 UTC 2019 retrying to connect to saas-mongo:27017 (3/5)
saas-mongoexp | /docker-entrypoint.sh: connect: Connection refused
saas-mongo | 2019-08-06T19:27:06.072+0000 I RECOVERY [initandlisten] WiredTiger recoveryTimestamp. Ts: Timestamp(0, 0)
saas-mongo | 2019-08-06T19:27:06.205+0000 I FTDC [initandlisten] Initializing full-time diagnostic data capture with directory '/data/db/diagnostic.data'
saas-mongoexp | /docker-entrypoint.sh: line 14: /dev/tcp/saas-mongo/27017: Connection refused
saas-mongo | 2019-08-06T19:27:06.207+0000 I NETWORK [initandlisten] waiting for connections on port 27017
saas-mongo | 2019-08-06T19:27:07.126+0000 I NETWORK [listener] connection accepted from 172.20.0.3:60070 #1 (1 connection now open)
saas-mongoexp | Tue Aug 6 19:27:07 UTC 2019 retrying to connect to saas-mongo:27017 (4/5)
saas-mongoexp | Welcome to mongo-express
saas-mongoexp | ------------------------
saas-mongoexp |
saas-mongoexp |
saas-mongoexp | Mongo Express server listening at http://0.0.0.0:8081
saas-mongoexp | Server is open to allow connections from anyone (0.0.0.0)
saas-mongo | 2019-08-06T19:27:07.127+0000 I NETWORK [conn1] end connection 172.20.0.3:60070 (0 connections now open)
saas-mongo | 2019-08-06T19:27:08.785+0000 I NETWORK [listener] connection accepted from 172.20.0.3:60072 #2 (1 connection now open)
saas-mongoexp | basicAuth credentials are "admin:pass", it is recommended you change this in your config.js!
saas-mongo | 2019-08-06T19:27:08.850+0000 I NETWORK [conn2] received client metadata from 172.20.0.3:60072 conn2: { driver: { name: "nodejs", version: "2.2.24" }, os: { type: "Linux", name: "linux", architecture: "x64", version: "4.14.134-boot2docker" }, platform: "Node.js v8.16.0, LE, mongodb-core: 2.1.8" }
saas-mongoexp | Database connected
saas-mongoexp | { MongoError: Authentication failed.
saas-mongoexp | at Function.MongoError.create (/node_modules/mongodb-core/lib/error.js:31:11)
saas-mongoexp | at /node_modules/mongodb-core/lib/connection/pool.js:483:72
saas-mongoexp | at authenticateStragglers (/node_modules/mongodb-core/lib/connection/pool.js:429:16)
saas-mongoexp | at Connection.messageHandler (/node_modules/mongodb-core/lib/connection/pool.js:463:5)
saas-mongoexp | at Socket. (/node_modules/mongodb-core/lib/connection/connection.js:319:22)
saas-mongoexp | at emitOne (events.js:116:13)
saas-mongoexp | at Socket.emit (events.js:211:7)
saas-mongoexp | at addChunk (_stream_readable.js:263:12)
saas-mongoexp | at readableAddChunk (_stream_readable.js:250:11)
saas-mongoexp | at Socket.Readable.push (_stream_readable.js:208:10)
saas-mongoexp | name: 'MongoError',
saas-mongoexp | message: 'Authentication failed.',
saas-mongoexp | ok: 0,
saas-mongoexp | errmsg: 'Authentication failed.',
saas-mongoexp | code: 18,
saas-mongoexp | codeName: 'AuthenticationFailed' }
saas-mongoexp | Admin Database connected
saas-mongoexp | unable to list databases
saas-api | $ node production-server/app.js
saas-mongo | 2019-08-06T19:27:08.880+0000 I ACCESS [conn2] SASL SCRAM-SHA-1 authentication failed for root on admin from client 172.20.0.3:60072 ; UserNotFound: Could not find user root@admin
saas-mongo | 2019-08-06T19:27:09.362+0000 I NETWORK [listener] connection accepted from 172.20.0.4:44914 #3 (2 connections now open)
saas-mongo | 2019-08-06T19:27:09.374+0000 I NETWORK [conn3] received client metadata from 172.20.0.4:44914 conn3: { driver: { name: "nodejs", version: "3.2.7" }, os: { type: "Linux", name: "linux", architecture: "x64", version: "4.14.134-boot2docker" }, platform: "Node.js v10.16.0, LE, mongodb-core: 3.2.7" }
saas-mongo | 2019-08-06T19:27:09.374+0000 I ACCESS [conn3] Supported SASL mechanisms requested for unknown user 'saas@saas'
saas-api | info: > Ready on http://api.saas.localhost:8000
saas-api | (node:27) UnhandledPromiseRejectionWarning: MongoNetworkError: failed to connect to server [saas-mongo:27017] on first connect [MongoError: Authentication failed.]
saas-api | at Pool. (/usr/src/api/node_modules/mongodb-core/lib/topologies/server.js:431:11)
saas-api | at Pool.emit (events.js:198:13)
saas-api | at Pool.EventEmitter.emit (domain.js:448:20)
saas-api | at connect (/usr/src/api/node_modules/mongodb-core/lib/connection/pool.js:557:14)
saas-api | at callback (/usr/src/api/node_modules/mongodb-core/lib/connection/connect.js:109:5)
saas-api | at provider.auth.err (/usr/src/api/node_modules/mongodb-core/lib/connection/connect.js:352:21)
saas-api | at _authenticateSingleConnection (/usr/src/api/node_modules/mongodb-core/lib/auth/auth_provider.js:66:11)
saas-api | at sendAuthCommand (/usr/src/api/node_modules/mongodb-core/lib/auth/scram.js:177:16)
saas-api | at Connection.messageHandler (/usr/src/api/node_modules/mongodb-core/lib/connection/connect.js:334:5)
saas-api | at Connection.emit (events.js:198:13)
saas-api | at Connection.EventEmitter.emit (domain.js:448:20)
saas-api | at processMessage (/usr/src/api/node_modules/mongodb-core/lib/connection/connection.js:364:10)
saas-api | at Socket. (/usr/src/api/node_modules/mongodb-core/lib/connection/connection.js:533:15)
saas-api | at Socket.emit (events.js:198:13)
saas-api | at Socket.EventEmitter.emit (domain.js:448:20)
saas-api | at addChunk (_stream_readable.js:288:12)
saas-api | (node:27) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). (rejection id: 1)
saas-mongo | 2019-08-06T19:27:09.394+0000 I ACCESS [conn3] SASL SCRAM-SHA-1 authentication failed for saas on saas from client 172.20.0.4:44914 ; UserNotFound: Could not find user saas@saas
saas-app | $ node production-server/server/app.js
saas-app | > Using external babel configuration
saas-app | > Location: "/usr/src/app/.babelrc"
saas-app | > Ready on http://app.saas.localhost:3000

Problem sign in - production with heroku

I'm using saas repo and trying to upload my code to Heroku, in my local machine everything works fine with google sign in. but I'm having a problem understanding what the PRODUCTION_URL_API env variable should be - my URL is https://[xxx].herokuapp.com and how do I redirect to my API URL
localhost:8000 in my dev environment
location file - /saas/app/lib/consts.ts

Screen Shot 2020-03-27 at 16 38 48

next.js v9

Are there any plans to upgrade to next.js v9? It has some cool new features like support for api routes. I was thinking this could get rid of the need to run 2 separate apps.

Postgres Support

Hi,
I’ve purchased your first book. I like the book. Do you have a any plan to support Postgres?

Cannot find SocketIOClient on deployment to Heroku

Upon deploying to Heroku, my build failed with the following message:

remote:        lib/store/index.ts(31,18): error TS2503: Cannot find namespace 'SocketIOClient'.
remote:        lib/store/index.ts(40,14): error TS2503: Cannot find namespace 'SocketIOClient'.
remote:        error Command failed with exit code 2.
remote:        info Visit https://yarnpkg.com/en/docs/cli/install for documentation about this command.
remote:        error Command failed with exit code 2.
remote:        info Visit https://yarnpkg.com/en/docs/cli/install for documentation about this command.

and Heroku rejected the build. I was able to solve this problem by manually moving my node_modules/@types folder to a local folder (in my case, app/lib) and redirecting typeRoots inside tsconfig towards the folder I moved it to, instead of node_modules.

This error should be reproducible by just taking a fresh copy of this repo, and following instructions to upload it to Heroku. I'm not exactly sure what the underlying problem is, as the app builds fine on my machine. Error occurs only upon deployment to Heroku.

Critical security issue in one node dependency: event-stream

Hello.

I'm not able to try this repository locally. Looks like there is a problem to install the node packages running the yarn command.

After running yarn in the api folder, the following error appears:

error https://registry.yarnpkg.com/event-stream/-/event-stream-3.3.6.tgz: Extracting tar content of undefined failed, thefile appears to be corrupt: "Unexpected end of data"
info Visit https://yarnpkg.com/en/docs/cli/install for documentation about this command.

Looks like it has something to do about this problem: https://blog.npmjs.org/post/180565383195/details-about-the-event-stream-incident

It can be checked by running the following command inside api folder: yarn audit

What do to?

Create more complete guide for suggested deployment method

First I'd like to say that this platform is great. It is exactly what I was looking for, a starting point that does not rely on too many frameworks within an ecosystem I am familiar with (React). Now that I have all parts of it set up, I can't wait to start hacking at it and creating something new and cool out of it.

That said, it did take me a good four days of fierce googling and debugging to get everything deployed. I'm coming from an Electron.js background, having done small-scale desktop and mobile development for the last while, and everything from Mailchimp to SES to S3 was new to me. I feel these things aren't obvious, and would love a doc to talk about it in closer detail, so others could hit the ground running faster. I realize there's a bit where you guys talk about how you publish one of the books, but it doesn't say anything about the third party services.

My method kind-of sort-of works -- haven't tested out Stripe API yet and haven't bugged SES to let me send unverified emails, but everything aside from that works. I did end up modifying a couple of things (in particular related to CORS issues, where my .herokuapp.com API address was breaking my own api.example.com cookies, even after following the custom domain registration). I don't know how right everything is. In particular, I have no idea what AWS Elastic Beanstalk is, which sounds like a deployment process that is different from what I did. That said, if the Elastic Beanstalk tutorial is still a ways out, I can put together a PR that'll get people from A to B after having just done that.

ESLint for TypeScript

Things to do to enable highlighting and auto-fixing of TS code by ESLint on VS code editor:

  • install extension: dbaeumer.vscode-eslint
  • set TS parsing and proper directory by adding settings to settings.json for Workspace:
  "editor.defaultFormatter": "dbaeumer.vscode-eslint",
  "editor.formatOnSave": true,
  "eslint.enable": true,
  "eslint.alwaysShowStatus": true,
  "eslint.autoFixOnSave": true,
  "eslint.validate": [
    "javascript",
    "javascriptreact",
    { "language": "typescript", "autoFix": true },
    { "language": "typescriptreact", "autoFix": true }
  ],
  "eslint.workingDirectories": [
		{ "directory": "./book/1-end/app", "changeProcessCWD": true },
	]
  • add .eslintrc.js to the root of your project, for example ./book/1-end/app
module.exports = {
  parser: "@typescript-eslint/parser",
  extends: ["plugin:@typescript-eslint/recommended", "prettier"],
  env: {
    "es6": true,
    "node": true
  },
  rules: {
    'prettier/prettier': [
      'error',
      {
        singleQuote: true,
        trailingComma: 'all',
        arrowParens: 'always',
        printWidth: 100,
        semi: true
      },
    ],
    '@typescript-eslint/no-unused-vars': 'off',
    '@typescript-eslint/explicit-function-return-type': 'off'
 },
  plugins: [
    "prettier"
  ]
}
  • add all necessary packages to devDependencies, for ./book/1-end/app, I have:
  "devDependencies": {
    "@types/node": "12.7.9",
    "@types/react": "16.9.4",
    "@types/react-dom": "16.9.1",
    "@typescript-eslint/eslint-plugin": "^2.4.0",
    "@typescript-eslint/parser": "^2.4.0",
    "eslint": "^6.5.1",
    "eslint-config-prettier": "^6.4.0",
    "eslint-plugin-prettier": "^3.1.1",
    "prettier": "^1.18.2"
  }
  • you may need to restart VS code editor
  • you can also add command "lint": "eslint . --ext .js,.jsx,.ts,.tsx" to package.json

Highlighting by ESLint:
Screenshot from 2019-10-20 12-55-34

To fix, press Ctrl + S.

Add your own formatting rules to .eslintrc.js

Send email for a new Post

  • When a new Post is created, send email to all members of Discussion.
  • Host code on AWS Lambda
  • Send email with AWS SES

This issue is part of #14


Steps:

  • At app:
  • (done) modify create/edit forms for Discussion,
  • (done) notificationType on store and server for Discussion,
  • (done) API method (sendEmailForNewPost) that sends request from app to Lambda via API Gateway
  • (done) create env var for LAMBDA_API_ENDPOINT and add it to app
  • (done) create email template for new Post at api
  • (done) email is sent when new Discussion is created, make sure that email is sent for new Post inside existing Discussion
  • At lambda: Lambda function that sends email to all members of Discussion except the user who created a new Post
    • (done) deployed and tested API Gateway (POST method)
    • (done) pass and print discussionName, authorName, postContent, userIds to sendEmailNotification function
    • (done) send email to each user from userIds

How do i reach out to the team?

I saw this in the readme.

If you are a small team who works best in a calm environment and builds a SaaS product that solves a real problem - we want to hear from you.

I bought the builderbook and I am interested in the saas template. So how do I reach you?

Cannot add an image to post

I think this might be two issues, please let me know if I should split this; but the crux of the problem is that adding images to posts does not work.

  1. After uploading the file, a notification is shown signifying a successful upload, but nothing else happens. This is because the ref for the MentionsInput is never set, which is how your code appends the div with the image in it. Simply adding inputRef={this.textAreaRef} after line 160 in PostEditor.ts makes that code work. However, the image still does not show up, because...

  2. The S3 must be misconfigured somewhere. If you go directly to the link that S3 gives you, you're served an AccessDenied error like this (https://saas-posts.s3.us-west-1.amazonaws.com/877/ud3vncvuzc594lc9okpo/03Tomopteris_helgolandica.jpg?AWSAccessKeyId=AKIAI3ZSOZXFCGWGWL7Q&Content-Type=image%2Fjpeg&Expires=1565717571&Signature=5p5%2FNA2cHdUutBC6ivvDtiDCqno%3D&x-amz-acl=private). I so far have not figured out why this part is breaking; putting posts into the team avatars bucket didn't solve the issue, even though team avatars upload fine. Unfortunately I was not at all familiar with S3 before beginning to work with SaaS boilerplate, so my knowledge here is limited.

I am able to reproduce this on my own deployment, as well as on Async Labs' test deployment (https://saas-app.async-await.com). The link above is to an image I tried to upload onto Async's bucket as part of a post. I've looked through existing issues, commit comments, and the TODO to see whether this is a delayed feature, but since it's not mentioned elsewhere, I must assume it is a bug.

Meaning for Issue labels

  • Assigned: Issue is assigned to at least one person.
  • Uphill: Issue is being actively discussed, implementation has not started.
  • Peak: Discussion is complete and Issue is ready for implementation.
  • Downhill: Issue is being actively implemented, interruptions are discouraged.
  • Ready to test: Issue is implemented and ready for testing.

yarn test failing

Hi There,

I'm new to React + TypeScript, thank you so much for this boilerplate, it will undoubtly allow me to learn a lot! :)

I did manage to get it all setup and building, however I was getting a fail on yarn test, on closer inspection the only test was a js, I migrated the code to TS and also made sure it ran.

Whilst doing that I came across a couple more issues (mostly with my IDE - vscode) so I also added a specific tsconfig on the test directory.

Anyway, all is in this fork https://github.com/pnmcosta/saas-boilerplate, and I'll happily PR it if you wish to. Please do note that mockingoose, ts-jest and @types/jest have been added to the api's devDeps.

Cheers,
P.

Retrieve Card function fails

I currently can't set up a card/subscription on the billing page.

I think this is because the types provided by @types/stripe are inaccurate -- there is no retrieveCard function on the customers object.

If I change the retrieveCard function in api/server/stripe.ts to the following, it seems to work (I've just been testing this with a test stripe account):

async function retrieveCard({ customerId, cardId }) {
  logger.debug(customerId);
  logger.debug(cardId);
  const cards = await stripeInstance.paymentMethods.list({
    customer: customerId,
    type: 'card',
  });
  return cards.data[0];
}

Admittedly, this doesn't fit the coding style at all 😄, and would only support a single payment method per user. It seems as if a new approach is needed to grab the card details.

Thanks for all of the time that has gone into this project, it's amazing.

Issue with Paswordless login

I have noticed a few issues with paswordless auth, are these to be considered bugs?

User joins via paswordless it says on the user screen that they signed up via google.

If user doesn't have a photo, thier name doesn't display on the team members list.

if a user is invited to join a team, then signs in paswordless with the same email it assumes they are creating a new account. If they then sign in using google they have lost access to that old team and now join that newly created account?

Exposed mongodb credentials

Potential security breach. Verified credentials in URL *redacted*:*redacted*@dedicated-dev-m10-us-west-1-shard-00-00-eoobe.mongodb.net in file /book/2-end/api/.note

You should recycle your credentials immediately and move secrets to environment variables to prevent them being committed to GitHub.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.