chatable

package module
v0.0.0-...-5dbc853 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Dec 18, 2015 License: BSD-3-Clause Imports: 12 Imported by: 0

README

chatable

A simple chat server

How to use it

Dependencies
  • postgresql >=9.3
  • redis >=2.8
Run it
  1. change db/dbconf.yml to have the correct credentials.
  2. run goose up [1] to run all migrations.
  3. create a configuration file in cmd/ (see development.json for example).
  4. run godep restore [2] to install all golang dependencies.
  5. run make local to run the server.

APIs

Authorization is using jwt protocol.

POST /api/register
  • params:

    • first_name: required
    • last_name: required
    • username: required
    • password: required
    • email: required
    • phone: required
  • json:

{
    'status': 'success',
    'data': [
        {
            'email': 'hello@example.com',
            'first_name': 'Bob',
            'last_name': 'Bobson',
            'phone_number': '0123456789',
            'token': {
                'access_key_id': 'AN5O6G5HYN4KFY7H2KM7XX4MKRI2RPNKUQELMQ54RITJBNS5RXKA',
                'created_at': '2015-09-03T02:08:08.558583633Z',
                'expires_at': '2015-10-03T02:08:08.558584401Z',
                'is_refreshable': true,
                'modified_at': '2015-09-03T02:08:08.5585851Z',
                'refresh_token': 'FAIMPUSXVQJAMRGBSSUJHIRRZC3ROLLOLPYT5VQOR5K6OJKFY62Q',
                'secret_access_key': '5GHV7INX2QZTG4RFE6LQMR34HWSF27IUUM55P65MFW4462SLC43A'
            },
            'username': 'bob'
        }
    ],
    'error': {
        'code': 200,
        'errors': null,
        'message': ''
    },
    'page': 1,
    'per_page': 10,
    'current_page': 1
}


POST /api/auth_token
  • params:

    • username: required
    • password: required
  • json:

{
    'status': 'success',
    'data': [
        {
            'access_key_id': 'R7JKSI4JJSFAANS4UTENUMGFCC4WLF2UPXWRJHJGJ6HWELFGJMEA',
            'created_at': '2015-09-03T02:15:31.533436161Z',
            'expires_at': '2015-10-03T02:15:31.533437208Z',
            'is_refreshable': true,
            'modified_at': '2015-09-03T02:15:31.533438047Z',
            'refresh_token': 'N547NOKQVBVFAR7BEZKE5FSLOB4XN5VTOYH3TMIXHTQ4O6KJ5F4Q',
            'secret_access_key': '3KHIVVGM4YNHFS7FAZQOUXM3ASBZTL23QXMHPMMNQPHMHZ4HRAHA'
        }
    ],
    'error': {
        'code': 200,
        'errors': null,
        'message': ''
    },
    'page': 1,
    'per_page': 10,
    'current_page': 1
}
DELETE /api/auth_token
  • authentication is required
GET /api/inbox
  • authentication is required

  • json


{
    'status': 'success',
    'data': [
        {
            'author_username': 'bob',
            'created_at': '2015-09-02T15:49:02.996617Z',
            'latest_message': 'hello'
        }
    ],
    'error': {
        'code': 200,
        'errors': None,
        'message': ''
    },
    'page': 1,
    'per_page': 10,
    'current_page': 1
}
GET /api/thread/[username]
  • authentication is required

  • json

{
    'status': 'success'
    'data': [
        {
            'author': 'bob',
            'created_at': '2015-09-02T15:49:02.994339Z',
            'message': 'hello',
            'message_type': 0,
            'recipient': 'alice'
        },
        {
            'author': 'alice',
            'created_at': '2015-09-02T15:40:02.994339Z',
            'message': 'Hi bob',
            'message_type': 0,
            'recipient': 'bob'
        },
    ],
    'error': {
        'code': 200,
        'errors': null,
        'message': ''
    },
    'page': 1,
    'per_page': 10,
    'current_page': 2
}
websocket
  • endpoint: /api/ws

  • authentication is required

  • send a packet:

{
    'author': 'bob',
    'recipient': 'alice',
    'message': 'hello'
}

  • receive a packet
{
    'author': 'bob',
    'recipient': 'alice',
    'message': 'hello',
    'message_type': 0,
    'created_at':'2015-09-03T03:13:12.817650473Z'
}

Errors

{
    'status': 'fail',
    'data': [],
    'error': {
        'code': 400,
        'errors': {'error': 'Some fileds are not unique'},
        'message': 'User error'
    },
    'page': 1,
    'per_page': 10,
    'current_page': 0
}

Architecture

  • Servers

    • server A
    • server B
    • server C
  • Users

    • bob
    • alice
  • Message queues

    • shared queue
    • A's queue
    • B's queue
    • C's queue
  • Workflow

    1. bob connects to server A.
    2. alice connects to server B, server C.
    3. bob sends a message to alice.
    4. the message is pushed to the shared queue.
    5. shared queue pushes the message to A's queue, B's queue, C's queue and persists the message to database.
    6. server A pops from A's queue and drops the message because alice is not connected; server B pops from B's queue and pushes to alice; server C pops from B's queue and pushes to alice.

[1]. go get bitbucket.org/liamstask/goose/cmd/goose

[2]. go get github.com/tools/godep

Documentation

Index

Constants

View Source
const (
	MessageTypeText = iota
	MessageTypePhoto
)
View Source
const (
	UserClassAdmin = "0"
	UserClassUser  = "1"
)
View Source
const (
	EnvelopesLimit = 1000
)
View Source
const (
	PerPage = 10
)

Variables

This section is empty.

Functions

func CompareHash

func CompareHash(encrypted, password string) bool

CompareHash compares encrypted hash with the plain string. Returns true if the hash is generated from the password.

func GenerateHash

func GenerateHash(password string) string

GenerateHash creates a hash from a given string using bcrypt with a cost which makes brute force cracking hard. bcrypt.DefaultCost uses over 0.1 second. Use MinCost (about 40ms) for now.

func GenerateRandomKey

func GenerateRandomKey() string

GenerateRandomKey generates random key with only alphabetical letters.

func NewEnvelope

func NewEnvelope(sender int, recipient int, msg string, t int) (*Envelope, *Envelope)

NewEnvelope creates the incoming and outgoing envelopes. The first envelope is the envelope on the sender's side, and the second envelope is the envelope on the receipt's side.

func NewThread

func NewThread(uid int, withuid int, author string, msg string) (*Thread, *Thread)

NewThread creates 2 new threads

Types

type AuthToken

type AuthToken struct {
	ID              int         `db:"id"`
	AccessKeyID     string      `db:"access_key_id"`
	SecretAccessKey string      `db:"secret_access_key"`
	RefreshToken    string      `db:"refresh_token"`
	CreatedAt       time.Time   `db:"created_at"`
	ExpiresAt       time.Time   `db:"expires_at"`
	ModifiedAt      time.Time   `db:"modified_at"`
	UserID          int         `db:"user_id"`
	ClientID        int         `db:"client_id"`
	IsActive        bool        `db:"is_active"`
	IsRefreshable   bool        `db:"is_refreshable"`
	Scope           StringSlice `db:"scope"`
}

func NewAuthToken

func NewAuthToken(uid int, cid int, scope StringSlice) *AuthToken

func (*AuthToken) IsGood

func (at *AuthToken) IsGood() bool

func (*AuthToken) ToPublicToken

func (at *AuthToken) ToPublicToken() *PublicToken

type AuthTokenService

type AuthTokenService interface {
	GetByAccessKeyID(key string) (*AuthToken, error)
	Create(at *AuthToken) error
	Update(at *AuthToken) (int64, error)
}

type AuthenticationError

type AuthenticationError struct {
	// contains filtered or unexported fields
}

func NewAuthenticationError

func NewAuthenticationError(msg string) AuthenticationError

func (AuthenticationError) Details

func (c AuthenticationError) Details() ErrorDetails

type CompoundError

type CompoundError interface {
	Error() string
	Details() ErrorDetails
}

func PersistEnvelope

PersistEnvelope saves thread and 2 envelopes from a PublicEnvelope.

type Envelope

type Envelope struct {
	ID          int      `db:"id"`
	UserID      int      `db:"user_id"`
	WithUserID  int      `db:"with_user_id"`
	IsIncoming  bool     `db:"is_incoming"`
	CreatedAt   NullTime `db:"created_at"`
	DeletedAt   NullTime `db:"deleted_at"`
	ReadAt      NullTime `db:"read_at"`
	Message     string   `db:"message"`
	MessageType int      `db:"message_type"`
}

Envelope represents a row in the envelopes table

func (*Envelope) IsDeleted

func (env *Envelope) IsDeleted() bool

func (*Envelope) IsRead

func (env *Envelope) IsRead() bool

func (*Envelope) ToPublic

func (env *Envelope) ToPublic(us UserService) *PublicEnvelope

type EnvelopeService

type EnvelopeService interface {
	GetByUserIDWithUserID(uid int, withuid int, offset int) ([]*Envelope,
		error)
	Create(env *Envelope) error
	MarkDelete(env *Envelope) (int64, error)
	MarkRead(env *Envelope) (int64, error)
}

EnvelopeService defines the protocol for envelopes

type ErrorDetails

type ErrorDetails map[string]string

type FormError

type FormError struct {
	// contains filtered or unexported fields
}

func NewFormError

func NewFormError(msg string, details ErrorDetails) FormError

func (FormError) Details

func (c FormError) Details() ErrorDetails

type JSONError

type JSONError struct {
	Code    int          `json:"code"`
	Message string       `json:"message"`
	Errors  ErrorDetails `json:"errors"`
}

type JSONResult

type JSONResult struct {
	Status      string      `json:"status"`
	Data        interface{} `json:"data"`
	Error       JSONError   `json:"error"`
	Page        int         `json:"page"`
	CurrentPage int         `json:"current_page"`
	PerPage     int         `json:"per_page"`
}

func NewErrorJSONResult

func NewErrorJSONResult(err JSONError) *JSONResult

func NewJSONResult

func NewJSONResult(v interface{}, page int) *JSONResult

NewJSONResult returns a unified JSON response.

type NullTime

type NullTime struct {
	Time  time.Time
	Valid bool // Valid is true if Time is not NULL
}

NullTime represents a time.Time that may be null. NullTime implements the sql.Scanner interface so it can be used as a scan destination, similar to sql.NullString.

func (*NullTime) Scan

func (nt *NullTime) Scan(src interface{}) error

Scan implements the Scanner interface.

func (NullTime) Value

func (nt NullTime) Value() (driver.Value, error)

Value implements the driver Valuer interface.

type PublicEnvelope

type PublicEnvelope struct {
	Author      string    `json:"author"`
	Recipient   string    `json:"recipient"`
	Message     string    `json:"message"`
	MessageType int       `json:"message_type"`
	CreatedAt   time.Time `json:"created_at"`
}

type PublicThread

type PublicThread struct {
	AuthorUsername string    `json:"author_username"`
	CreatedAt      time.Time `json:"created_at"`
	LatestMessage  string    `json:"latest_message"`
}

type PublicToken

type PublicToken struct {
	AccessKeyID     string    `json:"access_key_id"`
	SecretAccessKey string    `json:"secret_access_key"`
	RefreshToken    string    `json:"refresh_token"`
	CreatedAt       time.Time `json:"created_at"`
	ExpiresAt       time.Time `json:"expires_at"`
	ModifiedAt      time.Time `json:"modified_at"`
	IsRefreshable   bool      `json:"is_refreshable"`
}

type RdsService

type RdsService interface {
	Enqueue(queue string, env PublicEnvelope) CompoundError
	Dequeue(queue string) (PublicEnvelope, CompoundError)

	// QM: queue manager
	AddToQM(key string, queue string) CompoundError
	QMMembers(key string) ([]string, CompoundError)
	RemoveFromQM(key string, queue string) CompoundError
}

RdsService defines the protocol to use redis

type ServerError

type ServerError struct {
	// contains filtered or unexported fields
}

func NewServerError

func NewServerError(msg string) ServerError

func (ServerError) Details

func (c ServerError) Details() ErrorDetails

type StringSlice

type StringSlice []string

StringSlice is one dimension text array in Postgresql https://gist.github.com/adharris/4163702#gistcomment-1356268

func (*StringSlice) Scan

func (s *StringSlice) Scan(src interface{}) error

Scan convert to a slice of strings http://www.postgresql.org/docs/9.1/static/arrays.html#ARRAYS-IO

func (StringSlice) Value

func (s StringSlice) Value() (driver.Value, error)

type Thread

type Thread struct {
	ID             int      `db:"id"`
	UserID         int      `db:"user_id"`
	WithUserID     int      `db:"with_user_id"`
	AuthorUsername string   `db:"author_username"`
	CreatedAt      NullTime `db:"created_at"`
	LatestMessage  string   `db:"latest_message"`
}

Thread represents a row in the threads table

func (*Thread) ToPublic

func (t *Thread) ToPublic() *PublicThread

type ThreadService

type ThreadService interface {
	GetByUserID(uid int, offset int) ([]*Thread, error)
	Upsert(t *Thread) (int64, error)
}

ThreadService defines the protocol for threads

type User

type User struct {
	ID            int      `db:"id"`
	Username      string   `db:"username"`
	FirstName     string   `db:"first_name"`
	LastName      string   `db:"last_name"`
	Email         string   `db:"email"`
	PhoneNumber   string   `db:"phone_number"`
	Password      string   `db:"password"`
	IsActive      bool     `db:"is_active"`
	CreatedAt     NullTime `db:"created_at"`
	DeactivatedAt NullTime `db:"deactivated_at"`
	OriginalIP    string   `db:"original_ip"`
	UserClass     string   `db:"user_class"`
}

User is the corresponding type for a row in users table

func NewUser

func NewUser(fname, lname, uname, pass, email, phone, ip string) *User

NewUser creates a new user

type UserError

type UserError struct {
	// contains filtered or unexported fields
}

func NewUserError

func NewUserError(msg string) UserError

func (UserError) Details

func (c UserError) Details() ErrorDetails

type UserService

type UserService interface {
	GetByID(id int) (*User, error)
	GetByIDs(ids ...int) ([]*User, error)
	GetByUsername(uname string) (*User, error)
	Create(u *User) error
	Update(u *User) (int64, error)
}

UserService defines the protocol for users

type UserWithToken

type UserWithToken struct {
	FirstName   string      `json:"first_name"`
	LastName    string      `json:"last_name"`
	Username    string      `json:"username"`
	Email       string      `json:"email"`
	PhoneNumber string      `json:"phone_number"`
	Token       PublicToken `json:"token"`
}

Directories

Path Synopsis
Godeps
_workspace/src/github.com/coopernurse/gorp
Package gorp provides a simple way to marshal Go structs to and from SQL databases.
Package gorp provides a simple way to marshal Go structs to and from SQL databases.
_workspace/src/github.com/dgrijalva/jwt-go
Package jwt is a Go implementation of JSON Web Tokens: http://self-issued.info/docs/draft-jones-json-web-token.html See README.md for more info.
Package jwt is a Go implementation of JSON Web Tokens: http://self-issued.info/docs/draft-jones-json-web-token.html See README.md for more info.
A useful example app.
_workspace/src/github.com/garyburd/redigo/internal/redistest
Package redistest contains utilities for writing Redigo tests.
Package redistest contains utilities for writing Redigo tests.
_workspace/src/github.com/garyburd/redigo/redis
Package redis is a client for the Redis database.
Package redis is a client for the Redis database.
_workspace/src/github.com/go-errors/errors
Package errors provides errors that have stack-traces.
Package errors provides errors that have stack-traces.
_workspace/src/github.com/golang/glog
Package glog implements logging analogous to the Google-internal C++ INFO/ERROR/V setup.
Package glog implements logging analogous to the Google-internal C++ INFO/ERROR/V setup.
_workspace/src/github.com/gorilla/context
Package context stores values shared during a request lifetime.
Package context stores values shared during a request lifetime.
_workspace/src/github.com/gorilla/mux
Package gorilla/mux implements a request router and dispatcher.
Package gorilla/mux implements a request router and dispatcher.
_workspace/src/github.com/gorilla/securecookie
Package gorilla/securecookie encodes and decodes authenticated and optionally encrypted cookie values.
Package gorilla/securecookie encodes and decodes authenticated and optionally encrypted cookie values.
_workspace/src/github.com/gorilla/websocket
Package websocket implements the WebSocket protocol defined in RFC 6455.
Package websocket implements the WebSocket protocol defined in RFC 6455.
_workspace/src/github.com/gorilla/websocket/examples/autobahn
Command server is a test server for the Autobahn WebSockets Test Suite.
Command server is a test server for the Autobahn WebSockets Test Suite.
_workspace/src/github.com/lib/pq
Package pq is a pure Go Postgres driver for the database/sql package.
Package pq is a pure Go Postgres driver for the database/sql package.
_workspace/src/github.com/lib/pq/listen_example
Below you will find a self-contained Go program which uses the LISTEN / NOTIFY mechanism to avoid polling the database while waiting for more work to arrive.
Below you will find a self-contained Go program which uses the LISTEN / NOTIFY mechanism to avoid polling the database while waiting for more work to arrive.
_workspace/src/github.com/lib/pq/oid
Package oid contains OID constants as defined by the Postgres server.
Package oid contains OID constants as defined by the Postgres server.
_workspace/src/github.com/stretchr/testify/assert
Package assert provides a set of comprehensive testing tools for use with the normal Go testing system.
Package assert provides a set of comprehensive testing tools for use with the normal Go testing system.
_workspace/src/github.com/stretchr/testify/require
Alternative testing tools which stop test execution if test failed.
Alternative testing tools which stop test execution if test failed.
_workspace/src/github.com/stretchr/testify/suite
The suite package contains logic for creating testing suite structs and running the methods on those structs as tests.
The suite package contains logic for creating testing suite structs and running the methods on those structs as tests.
_workspace/src/golang.org/x/crypto/bcrypt
Package bcrypt implements Provos and Mazières's bcrypt adaptive hashing algorithm.
Package bcrypt implements Provos and Mazières's bcrypt adaptive hashing algorithm.
_workspace/src/golang.org/x/crypto/blowfish
Package blowfish implements Bruce Schneier's Blowfish encryption algorithm.
Package blowfish implements Bruce Schneier's Blowfish encryption algorithm.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL