All Projects → dstpierre → Gosaas

dstpierre / Gosaas

Licence: mit
A Go library making building SaaS and web app quicker. Part of my book https://buildsaasappingo.com

Programming Languages

go
31211 projects - #10 most used programming language

Labels

Projects that are alternatives of or similar to Gosaas

Saasify
The easiest way to monetize your API. 🚀
Stars: ✭ 912 (+744.44%)
Mutual labels:  saas
Catapult
💥 Catapult is a DevOps website management platform for development teams.
Stars: ✭ 64 (-40.74%)
Mutual labels:  saas
Gatsby Starter Saas Marketing
☁️ A simple one page marketing site starter for SaaS companies and indie hackers
Stars: ✭ 103 (-4.63%)
Mutual labels:  saas
Ha Db
探索高效的SAAS结构,单体应用动态切换数据库。
Stars: ✭ 44 (-59.26%)
Mutual labels:  saas
Djaoapp
User login, billing, access control as part of a session proxy
Stars: ✭ 61 (-43.52%)
Mutual labels:  saas
Awesome Checklists
A curated list of checklists. Inspired by the awesome list thing.
Stars: ✭ 71 (-34.26%)
Mutual labels:  saas
Pinax Stripe
a payments Django app for Stripe
Stars: ✭ 650 (+501.85%)
Mutual labels:  saas
Invoice As A Service
💰 Simple invoicing service (REST API): from JSON to PDF
Stars: ✭ 106 (-1.85%)
Mutual labels:  saas
Makeless Go
[BETA] Makeless - SaaS Ecosystem - Golang Implementation
Stars: ✭ 64 (-40.74%)
Mutual labels:  saas
Eugrade
Communication and 👬Collaboration Platform for 🏫Education | 高效全面的在线协作与教育管理系统
Stars: ✭ 99 (-8.33%)
Mutual labels:  saas
Wertik Js
💪 A library that powers your app with GraphQL + Rest API
Stars: ✭ 56 (-48.15%)
Mutual labels:  saas
Stack On A Budget
A collection of services with great free tiers for developers on a budget. Sponsored by Mockoon, the best mock API tool. https://mockoon.com
Stars: ✭ 10,836 (+9933.33%)
Mutual labels:  saas
Viewfinderjs
📷 ViewFinder - NodeJS product to make the browser into a web app. WTF RBI. CBII. Remote browser isolation, embeddable browserview, secure chrome saas. Licenses, managed, self-hosted. Like S2, WebGap, Bromium, Authentic8, Menlo Security and Broadcom, but open source with free live demos available now! Also, integrated RBI/CDR with CDR from https://github.com/dosyago/p2%2e
Stars: ✭ 1,175 (+987.96%)
Mutual labels:  saas
Silex
Silex is a static website builder in the cloud.
Stars: ✭ 958 (+787.04%)
Mutual labels:  saas
Oneplatform
onePlatform定位是企业级应用网关,提供提供服务路由、SSO、统一认证授权、统一日志、全局事件以及模块化管理等基础能力。基于Spring cloud、开箱即用、无额外学习成本、无缝对接老系统。→提供配套视频教程(Q群: 61859839)
Stars: ✭ 105 (-2.78%)
Mutual labels:  saas
Tenancy
Run multiple websites using the same Laravel installation while keeping tenant specific data separated for fully independent multi-domain setups.
Stars: ✭ 916 (+748.15%)
Mutual labels:  saas
Parabol
Free online agile retrospective meeting tool
Stars: ✭ 1,145 (+960.19%)
Mutual labels:  saas
Saas Vuejs Tailwindcss
VueJS + TailwindCSS frontend for SaaS apps.
Stars: ✭ 107 (-0.93%)
Mutual labels:  saas
Netcoresaas
Asp.Net Core multi-tenant application Sample using #SaaSKit
Stars: ✭ 105 (-2.78%)
Mutual labels:  saas
Baremetrics V1
This was the very first version of Baremetrics from 2013. It's published here for posterity.
Stars: ✭ 73 (-32.41%)
Mutual labels:  saas
Build a SaaS app in Go

gosaas Documentation CircleCI Go Report Card Maintainability

In September 2018 I published a book named Build a SaaS app in Go. This project is the transformation of what the book teaches into a library that can be used to quickly build a web app / SaaS and focusing on your core product instead of common SaaS components.

This is under development and API will change.

Migrating to PostgreSQL at the moment.

Usage quick example

You can create your main package and copy the docker-compose.yml. You'll need Redis and PostgreSQL for the library to work.

package main

import (
	"net/http"
	"github.com/dstpierre/gosaas"
	"github.com/dstpierre/gosaas/model"
)

func main() {
	routes := make(map[string]*gosaas.Route)
	routes["test"] = &gosaas.Route{
		Logger:      true,
		MinimumRole: model.RolePublic,
		Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			gosaas.Respond(w, r, http.StatusOK, "hello world!")
		}),
	}

	mux := gosaas.NewServer(routes)
	http.ListenAndServe(":8080", mux)
}

Than start the docker containers and your app:

$> docker-compose up
$> go run main.go

Than you request localhost:8080:

$> curl http://localhost:8080/test
"hello? world!"

Table of content

Installation

go get github.com/dstpierre/gosaas

What's included

The following aspects are covered by this library:

  • Web server capable of serving HTML templates, static files. Also JSON for an API.
  • Easy helper functions for parsing and encoding type<->JSON.
  • Routing logic in your own code.
  • Middlewares: logging, authentication, rate limiting and throttling.
  • User authentication and authorization using multiple ways to pass a token and a simple role based authorization.
  • Database agnostic data layer. Currently handling PostgreSQL.
  • User management, billing (per account or per user) and webhooks management. [in dev]
  • Simple queue (using Redis) and Pub/Sub for queuing tasks.
  • Cron-like scheduling for recurring tasks.

The in dev part means that those parts needs some refactoring compare to what was built in the book. The vast majority of the code is there and working, but it's not "library" friendly at the moment.

Quickstart

Here's some quick tips to get you up and running.

Defining routes

You only need to pass the top-level routes that gosaas needs to handle via a map[string]*gosaas.Route.

For example, if you have the following routes in your web application:

/task, /task/mine, /task/done, /ping

You would pass the following map to gosaas's NewServer function:

routes := make(map[string]*gosaas.Route)
routes["task"] = &gosaas.Route{
	Logger: true,
	WithDB: true,
	handler: task,
	...
}
routes["ping"] = &gosaas.Route(
	Logger: true,
	Handler: ping,
)

Where task and ping are types that implement http's ServeHTTP function, for instance:

type Task struct{}

func (t *Task) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	// you handle the rest of the routing logic in your own code
	var head string
	head, r.URL.Path = gosaas.ShiftPath(r.URL.Path)
	if head =="/" {
		t.list(w, r)
	} else if head == "mine" {
		t.mine(w, r)
	}
	...
}

You may define Task in its own package or inside your main package.

Each route can opt-in to include specific middleware, here's the list:

// Route represents a web handler with optional middlewares.
type Route struct {
	// middleware
	WithDB           bool // Adds the database connection to the request Context
	Logger           bool // Writes to the stdout request information
	EnforceRateLimit bool // Enforce the default rate and throttling limits

	// authorization
	MinimumRole model.Roles // Indicates the minimum role to access this route

	Handler http.Handler // The handler that will be executed
}

This is how you would handle parameterized route /task/detail/id-goes-here:

func (t *Task) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	var head string
	head, r.URL.Path = gosaas.ShiftPath(r.URL.Path)
	if head == "detail" {
		t.detail(w, r)
	}
}

func (t *Task) detail(w http.ResponseWriter, r *http.Request) {
	id, _ := gosaas.ShiftPath(r.URL.Path)
	// id = "id-goes-here
	// and now you may call the database and passing this id (probably with the AccountID and UserID)
	// from the Auth value of the request Context
}

Database

Before 2019/03/17 there's were a MongoDB implementation which has been removed to only support database/sql. PostgreSQL is currently the only supported driver.

The data package exposes a DB type that have a Connection field pointing to the database.

Before calling http.ListenAndServe you have to initialize the DB field of the Server type:

db := &data.DB{}

if err := db.Open(*dn, *ds); err != nil {
	log.Fatal("unable to connect to the database:", err)
}

mux.DB = db

Where *dn and *ds are flags containing "postgres" and "user=postgres password=postgres dbname=postgres sslmode=disable" for example, respectively which are the driver name and the datasource connection string.

This is an example of what your main function could be:

func main() {
	dn := flag.String("driver", "postgres", "name of the database driver to use, only postgres is supported at the moment")
	ds := flag.String("datasource", "", "database connection string")
	q := flag.Bool("queue", false, "set as queue pub/sub subscriber and task executor")
	e := flag.String("env", "dev", "set the current environment [dev|staging|prod]")
	flag.Parse()

	if len(*dn) == 0 || len(*ds) == 0 {
		flag.Usage()
		return
	}

	routes := make(map[string]*gosaas.Route)
	routes["test"] = &gosaas.Route{
		Logger:      true,
		MinimumRole: model.RolePublic,
		Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			gosaas.Respond(w, r, http.StatusOK, "hello? Worker!")
		}),
	}

	mux := gosaas.NewServer(routes)

	// open the database connection
	db := &data.DB{}

	if err := db.Open(*dn, *ds); err != nil {
		log.Fatal("unable to connect to the database:", err)
	}

	mux.DB = db

	isDev := false
	if *e == "dev" {
		isDev = true
	}

	// Set as pub/sub subscriber for the queue executor if q is true
	executors := make(map[queue.TaskID]queue.TaskExecutor)
	// if you have custom task executor you may fill this map with your own implementation 
	// of queue.taskExecutor interface
	cache.New(*q, isDev, executors)

	if err := http.ListenAndServe(":8080", mux); err != nil {
		log.Println(err)
	}

}

Responding to requests

The gosaas package exposes two useful functions:

Respond: used to return JSON:

gosaas.Respond(w, r, http.StatusOK, oneTask)

ServePage: used to return HTML from templates:

gosaas.ServePage(w, r, "template.html", data)

JSON parsing

There a helper function called gosaas.ParseBody that handles the JSON decoding into types. This is a typical http handler:

func (t Type) do(w http.ResponseWriter, r *http.Request) {
	var oneTask MyTask
	if err := gosaas.ParseBody(r.Body, &oneTask); err != nil {
		gosaas.Respond(w, r, http.StatusBadRequest, err)
		return
	}
	...
}

Context

You'll most certainly need to get a reference back to the database and the currently logged in user. This is done via the request Context.

func (t Type) list(w http.ResponseWriter, r *http.Request) {
	ctx := r.Context()
	db := ctx.Value(gosaas.ContextDatabase).(*data.DB)
	auth := ctx.Value(ContextAuth).(Auth)

	// you may use the db.Connection in your own data implementation
	tasks := Tasks{DB: db.Connection}
	list, err := tasks.List(auth.AccountID, auth.UserID)
	if err != nil {
		gosaas.Respond(w, r, http.StatusInternalServerError, err)
		return
	}
	Respond(w, r, http.StatusOK, list)
}

More documentation

You can find a more detailed documentation here: https://dominicstpierre.com/gosaas/

Please ask any questions here or on Twitter @dominicstpierre.

Status and contributing

I'm currently trying to reach a v1 and planning to use this in production with my next SaaS.

If you'd like to contribute I'd be more than happy to discuss, post an issue and feel free to explain what you'd like to add/change/remove.

Here's some aspect that are still a bit rough:

  • Not enough tests.
  • Redis is required and cannot be changed easily, it's also coupled with the queue package.
  • The controller for managing account/user is not done yet.
  • The billing controller will need to be glued.
  • The controllers package should be inside an internal package.
  • Still not sure if the way the data package is written that it is idiomatic / easy to understand.
  • There's no way to have granularity in the authorization, i.e. if /task require model.RoleUser /task/delete cannot have model.RoleAdmin as MinimumRole.

Running the tests

At this moment the tests uses the mem data implementation so you need to run the tests using the mem tag as follow:

$> go test -tags mem ./...

Credits

Thanks to the following packages:

Licence

MIT

Note that the project description data, including the texts, logos, images, and/or trademarks, for each open source project belongs to its rightful owner. If you wish to add or remove any projects, please contact us at [email protected].