├── hack
└── docker
│ ├── s6
│ ├── .s6-svscan
│ │ └── finish
│ └── backpulse-serve
│ │ ├── setup
│ │ └── run
│ ├── nsswitch.conf
│ ├── finalize.sh
│ └── start.sh
├── .gitignore
├── config.json.template
├── utils
├── stripe.go
├── jwt.go
├── utils.go
├── modules.go
├── sites.go
├── strings.go
├── config.go
├── users.go
├── response.go
└── googlecloud.go
├── models
├── Translation.go
├── Config.go
├── EmailVerification.go
├── AboutContent.go
├── File.go
├── Article.go
├── VideoGroup.go
├── Project.go
├── Album.go
├── ContactContent.go
├── Track.go
├── Video.go
├── Site.go
├── Gallery.go
├── User.go
└── Photo.go
├── routes
├── admin
│ ├── constants.go
│ ├── about.go
│ ├── contact.go
│ ├── projects.go
│ ├── files.go
│ ├── articles.go
│ ├── tracks.go
│ ├── videos.go
│ ├── albums.go
│ ├── videogroups.go
│ ├── photos.go
│ ├── users.go
│ ├── galleries.go
│ ├── admin.go
│ └── sites.go
├── routes.go
└── client
│ ├── client.go
│ └── documentation.md
├── handlers
├── admin
│ ├── constants.go
│ ├── about.go
│ ├── contact.go
│ ├── projects.go
│ ├── articles.go
│ ├── files.go
│ ├── tracks.go
│ ├── videogroups.go
│ ├── albums.go
│ ├── videos.go
│ ├── galleries.go
│ ├── photos.go
│ ├── users.go
│ └── sites.go
└── client
│ ├── about.go
│ ├── contact.go
│ ├── articles.go
│ ├── projects.go
│ ├── albums.go
│ ├── videos.go
│ └── galleries.go
├── .gitlab-ci.yml
├── constants
├── site_modules.go
└── languages.go
├── .env
├── database
├── about.go
├── contact.go
├── files.go
├── database.go
├── articles.go
├── videos.go
├── projects.go
├── tracks.go
├── albums.go
├── videogroups.go
├── photos.go
├── galleries.go
├── users.go
└── sites.go
├── go.mod
├── LICENSE
├── Dockerfile
├── main.go
├── strings.json
├── Makefile
├── README.md
└── CODE_OF_CONDUCT.md
/hack/docker/s6/.s6-svscan/finish:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | # Cleanup some services and s6 event folder
4 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | vendor/
2 | .idea
3 | *.exe
4 | backpulse
5 | config.json
6 | backpulse.json
7 | google_credentials.json
--------------------------------------------------------------------------------
/hack/docker/s6/backpulse-serve/setup:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | cd /app/backpulse
4 |
5 | # Link volumed data with app data
6 | ln -sfn /data/log log
7 |
--------------------------------------------------------------------------------
/hack/docker/s6/backpulse-serve/run:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | if test -f ./setup; then
4 | source ./setup
5 | fi
6 |
7 | export USER=backpulse
8 | exec gosu $USER /app/backpulse/backpulse -env /app/backpulse/.env
9 |
--------------------------------------------------------------------------------
/config.json.template:
--------------------------------------------------------------------------------
1 | {
2 | "URI": "mongodb://...",
3 | "Database": "-",
4 | "Secret": "-",
5 | "GmailAddress": "-",
6 | "GmailPassword": "-",
7 | "StripeKey": "-",
8 | "BucketName": "-"
9 | }
--------------------------------------------------------------------------------
/utils/stripe.go:
--------------------------------------------------------------------------------
1 | package utils
2 |
3 | import (
4 | "github.com/stripe/stripe-go/client"
5 | )
6 |
7 | var StripeClient client.API
8 |
9 | func InitStripe() {
10 | config := GetConfig()
11 | StripeClient.Init(config.StripeKey, nil)
12 | }
13 |
--------------------------------------------------------------------------------
/models/Translation.go:
--------------------------------------------------------------------------------
1 | package models
2 |
3 | // Translation struct
4 | type Translation struct {
5 | LanguageName string `json:"language_name" bson:"language_name"`
6 | LanguageCode string `json:"language_code" bson:"language_code"`
7 | Content string `json:"content" bson:"content"`
8 | }
9 |
--------------------------------------------------------------------------------
/routes/admin/constants.go:
--------------------------------------------------------------------------------
1 | package admin
2 |
3 | import (
4 | adminHandlers "github.com/backpulse/core/handlers/admin"
5 | "github.com/gorilla/mux"
6 | )
7 |
8 | func handleConstants(r *mux.Router) {
9 | r.HandleFunc("/constants/languages", adminHandlers.GetLanguages).Methods("GET")
10 | }
11 |
--------------------------------------------------------------------------------
/utils/jwt.go:
--------------------------------------------------------------------------------
1 | package utils
2 |
3 | import (
4 | "net/http"
5 |
6 | "github.com/dgrijalva/jwt-go"
7 | )
8 |
9 | //GetDecodedJWT Returns decoded JWT object
10 | func GetDecodedJWT(r *http.Request) jwt.MapClaims {
11 | return r.Context().Value("user").(*jwt.Token).Claims.(jwt.MapClaims)
12 | }
13 |
--------------------------------------------------------------------------------
/models/Config.go:
--------------------------------------------------------------------------------
1 | package models
2 |
3 | //Config config struct
4 | type Config struct {
5 | URI string
6 | Database string
7 | Secret string
8 | GmailAddress string
9 | GmailPassword string
10 | StripeKey string
11 | BucketName string
12 | BucketPubURL string
13 | }
14 |
--------------------------------------------------------------------------------
/utils/utils.go:
--------------------------------------------------------------------------------
1 | package utils
2 |
3 | import (
4 | "net/http"
5 | "strings"
6 | )
7 |
8 | // GetSubdomain : return subdomain of request
9 | func GetSubdomain(r *http.Request) string {
10 | fullHost := r.Host
11 | splitHost := strings.Split(fullHost, ".")
12 | if len(splitHost) < 1 {
13 | return ""
14 | }
15 | return splitHost[0]
16 | }
17 |
--------------------------------------------------------------------------------
/hack/docker/nsswitch.conf:
--------------------------------------------------------------------------------
1 | # /etc/nsswitch.conf
2 |
3 | passwd: compat
4 | group: compat
5 | shadow: compat
6 |
7 | hosts: files dns
8 | networks: files
9 |
10 | protocols: db files
11 | services: db files
12 | ethers: db files
13 | rpc: db files
14 |
15 | netgroup: nis
16 |
17 |
--------------------------------------------------------------------------------
/routes/admin/about.go:
--------------------------------------------------------------------------------
1 | package admin
2 |
3 | import (
4 | adminHandlers "github.com/backpulse/core/handlers/admin"
5 | "github.com/gorilla/mux"
6 | )
7 |
8 | func handleAbout(r *mux.Router) {
9 | r.Handle("/about/{name}", ProtectedRoute(adminHandlers.GetAbout)).Methods("GET")
10 | r.Handle("/about/{name}", ProtectedRoute(adminHandlers.UpdateAbout)).Methods("PUT")
11 | }
12 |
--------------------------------------------------------------------------------
/utils/modules.go:
--------------------------------------------------------------------------------
1 | package utils
2 |
3 | import (
4 | "github.com/backpulse/core/constants"
5 | )
6 |
7 | // CheckModuleExists : loop through list of modules to check if it included
8 | func CheckModuleExists(module string) bool {
9 | for _, m := range constants.Modules {
10 | if constants.Module(module) == m {
11 | return true
12 | }
13 | }
14 | return false
15 | }
16 |
--------------------------------------------------------------------------------
/handlers/admin/constants.go:
--------------------------------------------------------------------------------
1 | package adminhandlers
2 |
3 | import (
4 | "net/http"
5 |
6 | "github.com/backpulse/core/constants"
7 | "github.com/backpulse/core/utils"
8 | )
9 |
10 | // GetLanguages : return array of languages
11 | func GetLanguages(w http.ResponseWriter, r *http.Request) {
12 | utils.RespondWithJSON(w, http.StatusOK, "success", constants.Languages)
13 | }
14 |
--------------------------------------------------------------------------------
/.gitlab-ci.yml:
--------------------------------------------------------------------------------
1 | stages:
2 | - deploy
3 |
4 | production:
5 | stage: deploy
6 | script:
7 | - apt-get update -qy
8 | - apt-get install -y ruby-dev
9 | - gem install dpl
10 | - dpl --provider=heroku --app=backpulse --api-key=$HEROKU_API_KEY
11 | environment:
12 | name: Production
13 | url: https://backpulse.herokuapp.com/
14 | only:
15 | - master
16 | when: always
--------------------------------------------------------------------------------
/routes/admin/contact.go:
--------------------------------------------------------------------------------
1 | package admin
2 |
3 | import (
4 | adminHandlers "github.com/backpulse/core/handlers/admin"
5 | "github.com/gorilla/mux"
6 | )
7 |
8 | func handleContact(r *mux.Router) {
9 | r.Handle("/contact/{name}", ProtectedRoute(adminHandlers.GetContact)).Methods("GET")
10 | r.Handle("/contact/{name}", ProtectedRoute(adminHandlers.UpdateContact)).Methods("PUT")
11 | }
12 |
--------------------------------------------------------------------------------
/constants/site_modules.go:
--------------------------------------------------------------------------------
1 | package constants
2 |
3 | type Module string
4 |
5 | var Modules []Module = []Module{
6 | Galleries,
7 | Projects,
8 | Articles,
9 | Videos,
10 | Music,
11 | }
12 |
13 | const (
14 | Galleries Module = "galleries"
15 | Projects Module = "projects"
16 | Articles Module = "articles"
17 | Videos Module = "videos"
18 | Music Module = "music"
19 | )
20 |
--------------------------------------------------------------------------------
/utils/sites.go:
--------------------------------------------------------------------------------
1 | package utils
2 |
3 | import (
4 | "github.com/backpulse/core/models"
5 | )
6 |
7 | // HasPremiumFeatures : i don't know
8 | func HasPremiumFeatures(site models.Site, size float64) bool {
9 | //TODO: Look into this function
10 | if len(site.Collaborators) > 1 {
11 | return true
12 | }
13 | if site.TotalSize > 500 {
14 | return true
15 | }
16 | return false
17 | }
18 |
--------------------------------------------------------------------------------
/models/EmailVerification.go:
--------------------------------------------------------------------------------
1 | package models
2 |
3 | import (
4 | "time"
5 |
6 | "gopkg.in/mgo.v2/bson"
7 | )
8 |
9 | //EmailVerification struct
10 | type EmailVerification struct {
11 | ID bson.ObjectId `json:"id" bson:"_id"`
12 | UserID bson.ObjectId `json:"user_id" bson:"user_id"`
13 | Email string `json:"email" bson:"email"`
14 | ExpireAt time.Time `json:"expire_at" bson:"expire_at"`
15 | }
16 |
--------------------------------------------------------------------------------
/hack/docker/finalize.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | set -x
3 | set -e
4 |
5 | # Create user for backpulse
6 | addgroup -S backpulse
7 | adduser -G backpulse -H -D -g 'backpulse User' backpulse -h /data/backpulse -s /bin/bash && usermod -p '*' backpulse && passwd -u backpulse
8 | echo "export BACKPULSE_CUSTOM=${BACKPULSE_CUSTOM}" >> /etc/profile
9 |
10 | # Final cleaning
11 | rm /app/backpulse/docker/finalize.sh
12 | rm /app/backpulse/docker/nsswitch.conf
13 |
--------------------------------------------------------------------------------
/models/AboutContent.go:
--------------------------------------------------------------------------------
1 | package models
2 |
3 | import (
4 | "gopkg.in/mgo.v2/bson"
5 | )
6 |
7 | //AboutContent struct
8 | type AboutContent struct {
9 | ID bson.ObjectId `json:"id" bson:"_id"`
10 |
11 | SiteID bson.ObjectId `json:"site_id" bson:"site_id"`
12 | OwnerID bson.ObjectId `json:"owner_id" bson:"owner_id"`
13 |
14 | Name string `json:"name" bson:"name"`
15 | Descriptions []Translation `json:"descriptions" bson:"descriptions"`
16 | }
17 |
--------------------------------------------------------------------------------
/routes/routes.go:
--------------------------------------------------------------------------------
1 | package routes
2 |
3 | import (
4 | "github.com/backpulse/core/routes/admin"
5 | "github.com/backpulse/core/routes/client"
6 | "github.com/gorilla/mux"
7 | )
8 |
9 | //NewRouter creates router with route handlers
10 | func NewRouter() *mux.Router {
11 | r := mux.NewRouter()
12 |
13 | adminRouter := r.PathPrefix("/admin").Subrouter()
14 | admin.HandleAdmin(adminRouter)
15 |
16 | clientRouter := r.PathPrefix("/{name}").Subrouter()
17 | client.HandleClient(clientRouter)
18 |
19 | return r
20 | }
21 |
--------------------------------------------------------------------------------
/models/File.go:
--------------------------------------------------------------------------------
1 | package models
2 |
3 | import (
4 | "time"
5 |
6 | "gopkg.in/mgo.v2/bson"
7 | )
8 |
9 | // File struct
10 | type File struct {
11 | ID bson.ObjectId `json:"id" bson:"_id"`
12 | OwnerID bson.ObjectId `json:"owner_id" bson:"owner_id"`
13 | SiteID bson.ObjectId `json:"site_id" bson:"site_id"`
14 |
15 | Name string `json:"name" bson:"name"`
16 | URL string `json:"url" bson:"url"`
17 | Type string `json:"type" bson:"type"`
18 | Size float64 `json:"size" bson:"size"`
19 |
20 | CreatedAt time.Time `json:"created_at" bson:"created_at"`
21 | }
22 |
--------------------------------------------------------------------------------
/routes/admin/projects.go:
--------------------------------------------------------------------------------
1 | package admin
2 |
3 | import (
4 | adminHandlers "github.com/backpulse/core/handlers/admin"
5 | "github.com/gorilla/mux"
6 | )
7 |
8 | func handleProjects(r *mux.Router) {
9 | r.Handle("/project/{id}", ProtectedRoute(adminHandlers.GetProject)).Methods("GET")
10 | r.Handle("/projects/{name}", ProtectedRoute(adminHandlers.GetProjects)).Methods("GET")
11 |
12 | r.Handle("/projects/{name}", ProtectedRoute(adminHandlers.UpdateProject)).Methods("PUT")
13 | r.Handle("/project/{id}", ProtectedRoute(adminHandlers.DeleteProject)).Methods("DELETE")
14 | }
15 |
--------------------------------------------------------------------------------
/routes/admin/files.go:
--------------------------------------------------------------------------------
1 | package admin
2 |
3 | import (
4 | adminHandlers "github.com/backpulse/core/handlers/admin"
5 | "github.com/gorilla/mux"
6 | )
7 |
8 | func handleFiles(r *mux.Router) {
9 | r.Handle("/files/{name}", ProtectedRoute(adminHandlers.GetFiles)).Methods("GET")
10 | r.Handle("/files/{name}", ProtectedRoute(adminHandlers.UploadFile)).Methods("POST")
11 | r.Handle("/files/{name}/{id}/{filename}", ProtectedRoute(adminHandlers.UpdateFilename)).Methods("PUT")
12 | r.Handle("/files/{name}/{ids}", ProtectedRoute(adminHandlers.DeleteFiles)).Methods("DELETE")
13 | }
14 |
--------------------------------------------------------------------------------
/routes/admin/articles.go:
--------------------------------------------------------------------------------
1 | package admin
2 |
3 | import (
4 | adminHandlers "github.com/backpulse/core/handlers/admin"
5 | "github.com/gorilla/mux"
6 | )
7 |
8 | func handleArticles(r *mux.Router) {
9 | r.Handle("/articles/{name}", ProtectedRoute(adminHandlers.GetArticles)).Methods("GET")
10 | r.Handle("/articles/{name}/{id}", ProtectedRoute(adminHandlers.GetArticle)).Methods("GET")
11 | r.Handle("/articles/{name}", ProtectedRoute(adminHandlers.UpdateArticle)).Methods("PUT")
12 |
13 | r.Handle("/articles/{name}/{id}", ProtectedRoute(adminHandlers.DeleteArticle)).Methods("DELETE")
14 | }
15 |
--------------------------------------------------------------------------------
/hack/docker/start.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | create_volume_subfolder() {
4 | # Create VOLUME subfolder
5 | for f in /data/log; do
6 | if ! test -d $f; then
7 | mkdir -p $f
8 | fi
9 | done
10 | }
11 |
12 | setids() {
13 | PUID=${PUID:-1000}
14 | PGID=${PGID:-1000}
15 | groupmod -o -g "$PGID" backpulse
16 | usermod -o -u "$PUID" backpulse
17 | }
18 |
19 | setids
20 | create_volume_subfolder
21 |
22 | # Exec CMD or S6 by default if nothing present
23 | if [ $# -gt 0 ];then
24 | exec "$@"
25 | else
26 | exec /bin/s6-svscan /app/backpulse/docker/s6/
27 | fi
28 |
--------------------------------------------------------------------------------
/.env:
--------------------------------------------------------------------------------
1 | # MongoDB server address (_mongodb://..._)
2 | MONGODB_URI=mongodb://mongodb:27017
3 |
4 | # MongoDB database name
5 | DATABASE=backpulse
6 |
7 | # A secret key to encrypt JWT
8 | # SECRET=?
9 |
10 | # A gmail address if you wish to send confirmation emails
11 | # GMAIL_ADDRESS=?
12 |
13 | # The password associated with the gmail address obviously
14 | # GMAIL_PASSWORD=?
15 |
16 | # Your Stripe Key if you wish to integrate Stripe
17 | # STRIPE_KEY=?
18 |
19 | # Your Google Cloud Storage Bucket's name to store user files (images, binaries, plain text...)
20 | # BUCKET_NAME=?
21 |
22 | # Your Google Cloud Storage Bucket's public url
23 | # BUCKET_PUB_URL=?
--------------------------------------------------------------------------------
/models/Article.go:
--------------------------------------------------------------------------------
1 | package models
2 |
3 | import (
4 | "time"
5 |
6 | "gopkg.in/mgo.v2/bson"
7 | )
8 |
9 | // Article struct
10 | type Article struct {
11 | ID bson.ObjectId `json:"id" bson:"_id"`
12 | ShortID string `json:"short_id" bson:"short_id"`
13 |
14 | OwnerID bson.ObjectId `json:"owner_id" bson:"owner_id"`
15 | SiteID bson.ObjectId `json:"site_id" bson:"site_id"`
16 |
17 | Title string `json:"title" bson:"title"`
18 | Content string `json:"content" bson:"content"`
19 |
20 | Index int `json:"index" bson:"index"`
21 |
22 | CreatedAt time.Time `json:"created_at" bson:"created_at"`
23 | UpdatedAt time.Time `json:"updated_at" bson:"updated_at"`
24 | }
25 |
--------------------------------------------------------------------------------
/routes/admin/tracks.go:
--------------------------------------------------------------------------------
1 | package admin
2 |
3 | import (
4 | adminHandlers "github.com/backpulse/core/handlers/admin"
5 | "github.com/gorilla/mux"
6 | )
7 |
8 | func handleTracks(r *mux.Router) {
9 | r.Handle("/tracks/{name}/indexes", ProtectedRoute(adminHandlers.UpdateTracksIndexes)).Methods("PUT")
10 | r.Handle("/tracks/{name}/{albumid}", ProtectedRoute(adminHandlers.AddTrack)).Methods("POST")
11 |
12 | r.Handle("/tracks/{name}/{id}", ProtectedRoute(adminHandlers.GetTrack)).Methods("GET")
13 | r.Handle("/tracks/{name}/{id}", ProtectedRoute(adminHandlers.DeleteTrack)).Methods("DELETE")
14 |
15 | r.Handle("/tracks/{name}/{id}", ProtectedRoute(adminHandlers.UpdateTrack)).Methods("PUT")
16 | }
17 |
--------------------------------------------------------------------------------
/routes/admin/videos.go:
--------------------------------------------------------------------------------
1 | package admin
2 |
3 | import (
4 | adminHandlers "github.com/backpulse/core/handlers/admin"
5 | "github.com/gorilla/mux"
6 | )
7 |
8 | func handleVideos(r *mux.Router) {
9 | r.Handle("/videos/{name}/indexes", ProtectedRoute(adminHandlers.UpdateVideosIndexes)).Methods("PUT")
10 | r.Handle("/videos/{name}/{groupid}", ProtectedRoute(adminHandlers.AddVideo)).Methods("POST")
11 |
12 | r.Handle("/videos/{name}/{id}", ProtectedRoute(adminHandlers.GetVideo)).Methods("GET")
13 | r.Handle("/videos/{name}/{id}", ProtectedRoute(adminHandlers.DeleteVideo)).Methods("DELETE")
14 |
15 | r.Handle("/videos/{name}/{id}", ProtectedRoute(adminHandlers.UpdateVideo)).Methods("PUT")
16 | }
17 |
--------------------------------------------------------------------------------
/utils/strings.go:
--------------------------------------------------------------------------------
1 | package utils
2 |
3 | import (
4 | "encoding/json"
5 | "io/ioutil"
6 | "log"
7 | "net/http"
8 | "os"
9 | )
10 |
11 | //GetStrings get strings
12 | func GetStrings(r *http.Request) map[string]string {
13 | lang := GetLang(r)
14 | jsonFile, err := os.Open("./strings.json")
15 | if err != nil {
16 | log.Fatal(err)
17 | }
18 | defer jsonFile.Close()
19 |
20 | var data map[string]map[string]string
21 |
22 | byteValue, _ := ioutil.ReadAll(jsonFile)
23 | json.Unmarshal(byteValue, &data)
24 |
25 | if lang != "fr" {
26 | lang = "en"
27 | }
28 |
29 | return data[lang]
30 | }
31 |
32 | //GetLang returns user language
33 | func GetLang(r *http.Request) string {
34 | return r.Header.Get("Language")
35 | }
36 |
--------------------------------------------------------------------------------
/models/VideoGroup.go:
--------------------------------------------------------------------------------
1 | package models
2 |
3 | import (
4 | "time"
5 |
6 | "gopkg.in/mgo.v2/bson"
7 | )
8 |
9 | // VideoGroup struct
10 | type VideoGroup struct {
11 | ID bson.ObjectId `json:"id" bson:"_id"`
12 | ShortID string `json:"short_id" bson:"short_id"`
13 |
14 | OwnerID bson.ObjectId `json:"owner_id" bson:"owner_id"`
15 | SiteID bson.ObjectId `json:"site_id" bson:"site_id"`
16 |
17 | Title string `json:"title" bson:"title"`
18 | Image string `json:"image" bson:"image"`
19 | Videos []Video `json:"videos" bson:"videos"`
20 |
21 | Index int `json:"index" bson:"index"`
22 |
23 | CreatedAt time.Time `json:"created_at" bson:"created_at"`
24 | UpdatedAt time.Time `json:"updated_at" bson:"updated_at"`
25 | }
26 |
--------------------------------------------------------------------------------
/models/Project.go:
--------------------------------------------------------------------------------
1 | package models
2 |
3 | import (
4 | "time"
5 |
6 | "gopkg.in/mgo.v2/bson"
7 | )
8 |
9 | // Project struct
10 | type Project struct {
11 | ID bson.ObjectId `json:"id" bson:"_id"`
12 | ShortID string `json:"short_id" bson:"short_id"`
13 |
14 | OwnerID bson.ObjectId `json:"owner_id" bson:"owner_id"`
15 | SiteID bson.ObjectId `json:"site_id" bson:"site_id"`
16 |
17 | Title string `json:"title" bson:"title"`
18 | Titles []Translation `json:"titles" bson:"titles"`
19 | Descriptions []Translation `json:"descriptions" bson:"descriptions"`
20 |
21 | URL string `json:"url" bson:"url"`
22 |
23 | CreatedAt time.Time `json:"created_at" bson:"created_at"`
24 | UpdatedAt time.Time `json:"updated_at" bson:"updated_at"`
25 | }
26 |
--------------------------------------------------------------------------------
/handlers/client/about.go:
--------------------------------------------------------------------------------
1 | package clienthandlers
2 |
3 | import (
4 | "net/http"
5 |
6 | "github.com/gorilla/mux"
7 |
8 | "github.com/backpulse/core/database"
9 |
10 | "github.com/backpulse/core/utils"
11 | )
12 |
13 | // GetAbout : return about page
14 | func GetAbout(w http.ResponseWriter, r *http.Request) {
15 | vars := mux.Vars(r)
16 | name := vars["name"]
17 |
18 | site, err := database.GetSiteByName(name)
19 | if err != nil {
20 | utils.RespondWithJSON(w, http.StatusNotFound, "not_found", nil)
21 | return
22 | }
23 |
24 | about, err := database.GetAbout(site.ID)
25 | if err != nil {
26 | utils.RespondWithJSON(w, http.StatusNotFound, err.Error(), nil)
27 | return
28 | }
29 | utils.RespondWithJSON(w, http.StatusOK, "success", about)
30 | return
31 |
32 | }
33 |
--------------------------------------------------------------------------------
/routes/admin/albums.go:
--------------------------------------------------------------------------------
1 | package admin
2 |
3 | import (
4 | adminHandlers "github.com/backpulse/core/handlers/admin"
5 | "github.com/gorilla/mux"
6 | )
7 |
8 | func handleAlbums(r *mux.Router) {
9 |
10 | r.Handle("/albums/{name}/indexes", ProtectedRoute(adminHandlers.UpdateAlbumsIndexes)).Methods("PUT")
11 | r.Handle("/albums/{name}", ProtectedRoute(adminHandlers.CreateAlbum)).Methods("POST")
12 |
13 | r.Handle("/albums/{name}", ProtectedRoute(adminHandlers.GetAlbums)).Methods("GET")
14 | r.Handle("/albums/{name}/{id}", ProtectedRoute(adminHandlers.GetAlbum)).Methods("GET")
15 | r.Handle("/albums/{name}/{id}", ProtectedRoute(adminHandlers.UpdateAlbum)).Methods("PUT")
16 |
17 | r.Handle("/albums/{name}/{id}", ProtectedRoute(adminHandlers.DeleteAlbum)).Methods("DELETE")
18 | }
19 |
--------------------------------------------------------------------------------
/models/Album.go:
--------------------------------------------------------------------------------
1 | package models
2 |
3 | import (
4 | "time"
5 |
6 | "gopkg.in/mgo.v2/bson"
7 | )
8 |
9 | // Album struct
10 | type Album struct {
11 | ID bson.ObjectId `json:"id" bson:"_id"`
12 | ShortID string `json:"short_id" bson:"short_id"`
13 |
14 | OwnerID bson.ObjectId `json:"owner_id" bson:"owner_id"`
15 | SiteID bson.ObjectId `json:"site_id" bson:"site_id"`
16 |
17 | Cover string `json:"cover" bson:"cover"`
18 | Title string `json:"title" bson:"title"`
19 | Description string `json:"description" bson:"description"`
20 | Tracks []Track `json:"tracks" bson:"tracks"`
21 |
22 | Index int `json:"index" bson:"index"`
23 |
24 | CreatedAt time.Time `json:"created_at" bson:"created_at"`
25 | UpdatedAt time.Time `json:"updated_at" bson:"updated_at"`
26 | }
27 |
--------------------------------------------------------------------------------
/models/ContactContent.go:
--------------------------------------------------------------------------------
1 | package models
2 |
3 | import (
4 | "gopkg.in/mgo.v2/bson"
5 | )
6 |
7 | //ContactContent struct
8 | type ContactContent struct {
9 | ID bson.ObjectId `json:"id" bson:"_id"`
10 |
11 | SiteID bson.ObjectId `json:"site_id" bson:"site_id"`
12 | OwnerID bson.ObjectId `json:"owner_id" bson:"owner_id"`
13 |
14 | Name string `json:"name" bson:"name"`
15 | Phone string `json:"phone" bson:"phone"`
16 | Email string `json:"email" bson:"email"`
17 | Address string `json:"address" bson:"address"`
18 |
19 | FacebookURL string `json:"facebook_url" bson:"facebook_url"`
20 | InstagramURL string `json:"instagram_url" bson:"instagram_url"`
21 | TwitterURL string `json:"twitter_url" bson:"twitter_url"`
22 |
23 | CustomFields []string `json:"custom_fields" bson:"custom_fields"`
24 | }
25 |
--------------------------------------------------------------------------------
/database/about.go:
--------------------------------------------------------------------------------
1 | package database
2 |
3 | import (
4 | "github.com/backpulse/core/models"
5 | "gopkg.in/mgo.v2/bson"
6 | )
7 |
8 | // GetAbout : return about content of site
9 | func GetAbout(id bson.ObjectId) (models.AboutContent, error) {
10 | var about models.AboutContent
11 | err := DB.C(aboutCollection).Find(bson.M{
12 | "site_id": id,
13 | }).One(&about)
14 | return about, err
15 | }
16 |
17 | // UpdateAbout : update about content of site
18 | func UpdateAbout(id bson.ObjectId, about models.AboutContent) error {
19 | _, err := DB.C(aboutCollection).Upsert(bson.M{
20 | "site_id": id,
21 | }, bson.M{
22 | "$set": bson.M{
23 | "site_id": id,
24 | "owner_id": about.OwnerID,
25 | "name": about.Name,
26 | "descriptions": about.Descriptions,
27 | },
28 | })
29 | return err
30 | }
31 |
--------------------------------------------------------------------------------
/handlers/client/contact.go:
--------------------------------------------------------------------------------
1 | package clienthandlers
2 |
3 | import (
4 | "log"
5 | "net/http"
6 |
7 | "github.com/gorilla/mux"
8 |
9 | "github.com/backpulse/core/database"
10 |
11 | "github.com/backpulse/core/utils"
12 | )
13 |
14 | // GetContact : return contact page
15 | func GetContact(w http.ResponseWriter, r *http.Request) {
16 | vars := mux.Vars(r)
17 | name := vars["name"]
18 |
19 | site, err := database.GetSiteByName(name)
20 | if err != nil {
21 | utils.RespondWithJSON(w, http.StatusNotFound, "not_found", nil)
22 | return
23 | }
24 |
25 | contact, err := database.GetContact(site.ID)
26 | if err != nil {
27 | log.Println(err)
28 | utils.RespondWithJSON(w, http.StatusNotFound, err.Error(), nil)
29 | return
30 | }
31 | utils.RespondWithJSON(w, http.StatusOK, "success", contact)
32 | return
33 | }
34 |
--------------------------------------------------------------------------------
/models/Track.go:
--------------------------------------------------------------------------------
1 | package models
2 |
3 | import (
4 | "time"
5 |
6 | "gopkg.in/mgo.v2/bson"
7 | )
8 |
9 | // Track struct
10 | type Track struct {
11 | ID bson.ObjectId `json:"id" bson:"_id"`
12 | ShortID string `json:"short_id" bson:"short_id"`
13 |
14 | OwnerID bson.ObjectId `json:"owner_id" bson:"owner_id"`
15 | SiteID bson.ObjectId `json:"site_id" bson:"site_id"`
16 |
17 | AlbumID bson.ObjectId `json:"album_id" bson:"album_id"`
18 |
19 | Image string `json:"image" bson:"image"`
20 | Title string `json:"title" bson:"title"`
21 | URL string `json:"url" bson:"url"`
22 | Content string `json:"content" bson:"content"`
23 |
24 | Index int `json:"index" bson:"index"`
25 |
26 | CreatedAt time.Time `json:"created_at" bson:"created_at"`
27 | UpdatedAt time.Time `json:"updated_at" bson:"updated_at"`
28 | }
29 |
--------------------------------------------------------------------------------
/models/Video.go:
--------------------------------------------------------------------------------
1 | package models
2 |
3 | import (
4 | "time"
5 |
6 | "gopkg.in/mgo.v2/bson"
7 | )
8 |
9 | // Video struct
10 | type Video struct {
11 | ID bson.ObjectId `json:"id" bson:"_id"`
12 | ShortID string `json:"short_id" bson:"short_id"`
13 |
14 | OwnerID bson.ObjectId `json:"owner_id" bson:"owner_id"`
15 | SiteID bson.ObjectId `json:"site_id" bson:"site_id"`
16 |
17 | Title string `json:"title" bson:"title"`
18 | Content string `json:"content" bson:"content"`
19 | YouTubeURL string `json:"youtube_url" bson:"youtube_url"`
20 |
21 | VideoGroupID bson.ObjectId `json:"video_group_id" bson:"video_group_id"`
22 | Index int `json:"index" bson:"index"`
23 |
24 | CreatedAt time.Time `json:"created_at" bson:"created_at"`
25 | UpdatedAt time.Time `json:"updated_at" bson:"updated_at"`
26 | }
27 |
--------------------------------------------------------------------------------
/routes/admin/videogroups.go:
--------------------------------------------------------------------------------
1 | package admin
2 |
3 | import (
4 | adminHandlers "github.com/backpulse/core/handlers/admin"
5 | "github.com/gorilla/mux"
6 | )
7 |
8 | func handleVideoGroups(r *mux.Router) {
9 |
10 | r.Handle("/videogroups/{name}/indexes", ProtectedRoute(adminHandlers.UpdateVideoGroupsIndexes)).Methods("PUT")
11 | r.Handle("/videogroups/{name}", ProtectedRoute(adminHandlers.CreateVideoGroup)).Methods("POST")
12 |
13 | r.Handle("/videogroups/{name}", ProtectedRoute(adminHandlers.GetVideoGroups)).Methods("GET")
14 | r.Handle("/videogroups/{name}/{id}", ProtectedRoute(adminHandlers.GetVideoGroup)).Methods("GET")
15 | r.Handle("/videogroups/{name}/{id}", ProtectedRoute(adminHandlers.UpdateVideoGroup)).Methods("PUT")
16 |
17 | r.Handle("/videogroups/{name}/{id}", ProtectedRoute(adminHandlers.DeleteVideoGroup)).Methods("DELETE")
18 | }
19 |
--------------------------------------------------------------------------------
/routes/admin/photos.go:
--------------------------------------------------------------------------------
1 | package admin
2 |
3 | import (
4 | adminHandlers "github.com/backpulse/core/handlers/admin"
5 | "github.com/gorilla/mux"
6 | )
7 |
8 | func handlePhotos(r *mux.Router) {
9 | r.Handle("/photos/{name}/create", ProtectedRoute(adminHandlers.CreatePhoto)).Methods("POST")
10 | r.Handle("/photos/{name}", ProtectedRoute(adminHandlers.GetPhotos)).Methods("GET")
11 | r.Handle("/photos/{name}/{id}", ProtectedRoute(adminHandlers.GetPhoto)).Methods("GET")
12 | r.Handle("/photos/{name}/{id}", ProtectedRoute(adminHandlers.UpdatePhoto)).Methods("PUT")
13 | r.Handle("/photos/{name}", ProtectedRoute(adminHandlers.UploadPhoto)).Methods("POST")
14 | r.Handle("/photos/{name}/{id}", ProtectedRoute(adminHandlers.UpdatePhotoFile)).Methods("POST")
15 | r.Handle("/photos/{ids}", ProtectedRoute(adminHandlers.DeletePhotos)).Methods("DELETE")
16 |
17 | r.Handle("/photos/{name}/{id}/indexes", ProtectedRoute(adminHandlers.UpdatePhotosIndexes)).Methods("PUT")
18 | }
19 |
--------------------------------------------------------------------------------
/routes/admin/users.go:
--------------------------------------------------------------------------------
1 | package admin
2 |
3 | import (
4 | adminHandlers "github.com/backpulse/core/handlers/admin"
5 | "github.com/gorilla/mux"
6 | )
7 |
8 | func handleUsers(r *mux.Router) {
9 | r.HandleFunc("/users", adminHandlers.CreateUser).Methods("POST")
10 | r.HandleFunc("/users/authenticate", adminHandlers.AuthenticateUser).Methods("POST")
11 | r.HandleFunc("/users/verify/{id}", adminHandlers.VerifyUser).Methods("POST")
12 |
13 | r.Handle("/user", ProtectedRoute(adminHandlers.DeleteUser)).Methods("DELETE")
14 | r.Handle("/user/password", ProtectedRoute(adminHandlers.UpdateUserPassword)).Methods("PUT")
15 | r.Handle("/profile", ProtectedRoute(adminHandlers.UpdateUser)).Methods("PUT")
16 |
17 | r.Handle("/account/charge", ProtectedRoute(adminHandlers.ChargeAccount)).Methods("POST")
18 | r.Handle("/account/subscription", ProtectedRoute(adminHandlers.RemoveSubscription)).Methods("DELETE")
19 | r.Handle("/user", ProtectedRoute(adminHandlers.GetSelfUser)).Methods("GET")
20 | }
21 |
--------------------------------------------------------------------------------
/routes/admin/galleries.go:
--------------------------------------------------------------------------------
1 | package admin
2 |
3 | import (
4 | adminHandlers "github.com/backpulse/core/handlers/admin"
5 | "github.com/gorilla/mux"
6 | )
7 |
8 | func handleGalleries(r *mux.Router) {
9 | r.Handle("/gallery/{id}", ProtectedRoute(adminHandlers.GetGallery)).Methods("GET")
10 | r.Handle("/gallery/{id}", ProtectedRoute(adminHandlers.DeleteGallery)).Methods("DELETE")
11 | r.Handle("/gallery/{id}", ProtectedRoute(adminHandlers.UpdateGallery)).Methods("PUT")
12 |
13 | r.Handle("/galleries/{name}/{galleryID}/preview/{id}", ProtectedRoute(adminHandlers.SetGalleryPreview)).Methods("PUT")
14 | r.Handle("/galleries/{name}/default/{id}", ProtectedRoute(adminHandlers.SetDefaultGallery)).Methods("PUT")
15 | r.Handle("/galleries/{name}/indexes", ProtectedRoute(adminHandlers.UpdateGalleriesIndexes)).Methods("PUT")
16 | r.Handle("/galleries/{name}/{galleryName}", ProtectedRoute(adminHandlers.CreateGallery)).Methods("POST")
17 | r.Handle("/galleries/{name}", ProtectedRoute(adminHandlers.GetGalleries)).Methods("GET")
18 | }
19 |
--------------------------------------------------------------------------------
/models/Site.go:
--------------------------------------------------------------------------------
1 | package models
2 |
3 | import (
4 | "time"
5 |
6 | "github.com/backpulse/core/constants"
7 | "gopkg.in/mgo.v2/bson"
8 | )
9 |
10 | // Site struct
11 | type Site struct {
12 | ID bson.ObjectId `json:"id" bson:"_id"`
13 | OwnerID bson.ObjectId `json:"owner_id" bson:"owner_id"`
14 |
15 | DisplayName string `json:"display_name" bson:"display_name"`
16 | Name string `json:"name" bson:"name"`
17 |
18 | Modules []constants.Module `json:"modules" bson:"modules"`
19 |
20 | /* Emails */
21 | Collaborators []Collaborator `json:"collaborators" bson:"collaborators"`
22 |
23 | CreatedAt time.Time `json:"created_at" bson:"created_at"`
24 | UpdatedAt time.Time `json:"updated_at" bson:"updated_at"`
25 |
26 | /* Dynamic data */
27 | Favorite bool `json:"favorite" bson:"-"`
28 | Role string `json:"role" bson:"-"`
29 | TotalSize float64 `json:"total_size" bson:"-"`
30 | }
31 |
32 | type Collaborator struct {
33 | Email string `json:"email" bson:"email"`
34 | Role string `json:"role" bson:"role"`
35 | }
36 |
--------------------------------------------------------------------------------
/go.mod:
--------------------------------------------------------------------------------
1 | module github.com/backpulse/core
2 |
3 | go 1.11
4 |
5 | require (
6 | cloud.google.com/go v0.37.2
7 | github.com/asaskevich/govalidator v0.0.0-20180315120708-ccb8e960c48f
8 | github.com/auth0/go-jwt-middleware v0.0.0-20170425171159-5493cabe49f7
9 | github.com/dgrijalva/jwt-go v3.2.0+incompatible
10 | github.com/gorilla/context v1.1.1
11 | github.com/gorilla/mux v1.6.2
12 | github.com/joho/godotenv v1.3.0
13 | github.com/konsorten/go-windows-terminal-sequences v1.0.1
14 | github.com/rs/cors v1.6.0
15 | github.com/sirupsen/logrus v1.3.0
16 | github.com/stripe/stripe-go v59.1.0+incompatible
17 | github.com/teris-io/shortid v0.0.0-20160104014424-6c56cef5189c
18 | github.com/urfave/negroni v1.0.0
19 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2
20 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a
21 | google.golang.org/api v0.3.0
22 | gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc
23 | gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df
24 | gopkg.in/mgo.v2 v2.0.0-20180705113604-9856a29383ce
25 | )
26 |
--------------------------------------------------------------------------------
/database/contact.go:
--------------------------------------------------------------------------------
1 | package database
2 |
3 | import (
4 | "github.com/backpulse/core/models"
5 | "gopkg.in/mgo.v2/bson"
6 | )
7 |
8 | // UpdateContact : update contact content
9 | func UpdateContact(id bson.ObjectId, content models.ContactContent) error {
10 | _, err := DB.C(contactCollection).Upsert(bson.M{
11 | "site_id": id,
12 | }, bson.M{
13 | "$set": bson.M{
14 | "site_id": id,
15 | "owner_id": content.OwnerID,
16 | "name": content.Name,
17 | "phone": content.Phone,
18 | "email": content.Email,
19 | "address": content.Address,
20 | "facebook_url": content.FacebookURL,
21 | "instagram_url": content.InstagramURL,
22 | "twitter_url": content.TwitterURL,
23 | },
24 | })
25 | return err
26 | }
27 |
28 | // GetContact : return contact content
29 | func GetContact(id bson.ObjectId) (models.ContactContent, error) {
30 | var contactContent models.ContactContent
31 | err := DB.C(contactCollection).Find(bson.M{
32 | "site_id": id,
33 | }).One(&contactContent)
34 | return contactContent, err
35 | }
36 |
--------------------------------------------------------------------------------
/models/Gallery.go:
--------------------------------------------------------------------------------
1 | package models
2 |
3 | import (
4 | "time"
5 |
6 | "gopkg.in/mgo.v2/bson"
7 | )
8 |
9 | // Gallery struct
10 | type Gallery struct {
11 | ID bson.ObjectId `json:"id" bson:"_id"`
12 | ShortID string `json:"short_id" bson:"short_id"`
13 |
14 | OwnerID bson.ObjectId `json:"owner_id" bson:"owner_id"`
15 | SiteID bson.ObjectId `json:"site_id" bson:"site_id"`
16 |
17 | DefaultGallery bool `json:"default_gallery" bson:"default_gallery"`
18 |
19 | PreviewPhoto Photo `json:"preview_photo" bson:"-"`
20 | PreviewPhotoID bson.ObjectId `json:"preview_photo_id" bson:"preview_photo_id,omitempty"`
21 |
22 | Title string `json:"title" bson:"title"`
23 | Titles []Translation `json:"titles" bson:"titles"`
24 | Descriptions []Translation `json:"descriptions" bson:"descriptions"`
25 |
26 | Photos []Photo `json:"photos" bson:"photos,omitempty"`
27 |
28 | Index int `json:"index" bson:"index"`
29 |
30 | CreatedAt time.Time `json:"created_at" bson:"created_at"`
31 | UpdatedAt time.Time `json:"updated_at" bson:"updated_at"`
32 | }
33 |
--------------------------------------------------------------------------------
/models/User.go:
--------------------------------------------------------------------------------
1 | package models
2 |
3 | import (
4 | "time"
5 |
6 | "gopkg.in/mgo.v2/bson"
7 | )
8 |
9 | //User struct
10 | type User struct {
11 | ID bson.ObjectId `json:"id" bson:"_id,omitempty"`
12 |
13 | FullName string `json:"fullname" bson:"fullname"`
14 | Email string `json:"email" bson:"email"`
15 | Password string `json:"password" bson:"password"`
16 |
17 | Country string `json:"country" bson:"country"`
18 | City string `json:"city" bson:"city"`
19 | Address string `json:"address" bson:"address"`
20 | ZIP string `json:"zip" bson:"zip"`
21 | State string `json:"state" bson:"state"`
22 |
23 | EmailVerified bool `json:"email_verified" bson:"email_verified"`
24 |
25 | StripeID string `json:"-" bson:"stripe_id"`
26 | ActiveSubscriptionID string `json:"-" bson:"active_subscription_id"`
27 | Professional bool `json:"professional" bson:"professional"`
28 |
29 | CreatedAt time.Time `json:"created_at" bson:"created_at"`
30 | UpdatedAt time.Time `json:"updated_at" bson:"updated_at"`
31 |
32 | FavoriteSites []bson.ObjectId `json:"favorite_sites" bson:"favorite_sites"`
33 | }
34 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2019 Backpulse
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/database/files.go:
--------------------------------------------------------------------------------
1 | package database
2 |
3 | import (
4 | "github.com/backpulse/core/models"
5 | "gopkg.in/mgo.v2/bson"
6 | )
7 |
8 | // InsertFile : create file in db
9 | func InsertFile(file models.File) error {
10 | err := DB.C(filesCollection).Insert(file)
11 | return err
12 | }
13 |
14 | // GetSiteFiles : return array of file for site
15 | func GetSiteFiles(id bson.ObjectId) ([]models.File, error) {
16 | var files []models.File
17 | err := DB.C(filesCollection).Find(bson.M{
18 | "site_id": id,
19 | }).All(&files)
20 | return files, err
21 | }
22 |
23 | // DeleteFile : remove file from db
24 | func DeleteFile(fileID bson.ObjectId, siteID bson.ObjectId) error {
25 | err := DB.C(filesCollection).Remove(bson.M{
26 | "_id": fileID,
27 | "site_id": siteID,
28 | })
29 | return err
30 | }
31 |
32 | // UpdateFilename : change the name of a file
33 | func UpdateFilename(fileID bson.ObjectId, filename string, siteID bson.ObjectId) error {
34 | err := DB.C(filesCollection).Update(bson.M{
35 | "_id": fileID,
36 | "site_id": siteID,
37 | }, bson.M{
38 | "$set": bson.M{
39 | "name": filename,
40 | },
41 | })
42 | return err
43 | }
44 |
--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM golang:alpine AS binaryBuilder
2 | # Install build deps
3 | RUN apk --no-cache --no-progress add --virtual build-deps build-base git
4 |
5 | # Build project
6 | WORKDIR /go/src/github.com/backpulse/core
7 | COPY . .
8 | RUN make build
9 |
10 | FROM alpine:latest
11 | # Install system utils & runtime dependencies
12 | ADD https://github.com/tianon/gosu/releases/download/1.11/gosu-amd64 /usr/sbin/gosu
13 | RUN chmod +x /usr/sbin/gosu \
14 | && echo http://dl-2.alpinelinux.org/alpine/edge/community/ >> /etc/apk/repositories \
15 | && apk --no-cache --no-progress add bash s6 shadow
16 |
17 | # Configure LibC Name Service
18 | COPY hack/docker/nsswitch.conf /etc/nsswitch.conf
19 |
20 | # Copy target app from binaryBuilder stage
21 | WORKDIR /app/backpulse
22 | COPY hack/docker docker
23 | COPY .env ./
24 | COPY --from=binaryBuilder /go/src/github.com/backpulse/core/backpulse .
25 |
26 | # Finalize s6 configure
27 | RUN ./docker/finalize.sh
28 |
29 | # Configure Docker Container
30 | VOLUME ["/data"]
31 |
32 | # backend data interface agent.
33 | EXPOSE 8000
34 |
35 | ENTRYPOINT ["/app/backpulse/docker/start.sh"]
36 | CMD ["/bin/s6-svscan", "/app/backpulse/docker/s6/"]
37 |
--------------------------------------------------------------------------------
/models/Photo.go:
--------------------------------------------------------------------------------
1 | package models
2 |
3 | import (
4 | "time"
5 |
6 | "gopkg.in/mgo.v2/bson"
7 | )
8 |
9 | // Photo struct
10 | type Photo struct {
11 | ID bson.ObjectId `json:"id" bson:"_id"`
12 | OwnerID bson.ObjectId `json:"owner_id" bson:"owner_id"`
13 | SiteID bson.ObjectId `json:"site_id" bson:"site_id"`
14 |
15 | Title string `json:"title" bson:"title"`
16 | Content string `json:"content" bson:"content"`
17 |
18 | URL string `json:"url" bson:"url"`
19 | Width int `json:"width" bson:"width"`
20 | Height int `json:"height" bson:"height"`
21 | Format string `json:"format" bson:"format"`
22 |
23 | GalleryName string `json:"gallery_name,omitempty" bson:"gallery_name,omitempty"`
24 |
25 | IsGallery bool `json:"is_gallery" bson:"is_gallery"`
26 | GalleryID *bson.ObjectId `json:"gallery_id,omitempty" bson:"gallery_id,omitempty"`
27 |
28 | IsProject bool `json:"is_project" bson:"is_project"`
29 | ProjectID bson.ObjectId `json:"project_id" bson:"project_id,omitempty"`
30 | Size float64 `json:"size" bson:"size"`
31 |
32 | Index int `json:"index" bson:"index"`
33 |
34 | CreatedAt time.Time `json:"created_at" bson:"created_at"`
35 | }
36 |
--------------------------------------------------------------------------------
/utils/config.go:
--------------------------------------------------------------------------------
1 | package utils
2 |
3 | import (
4 | "encoding/json"
5 | "os"
6 |
7 | "github.com/backpulse/core/models"
8 | )
9 |
10 | func loadConfiguration(file string) (models.Config, error) {
11 | var config models.Config
12 | configFile, err := os.Open(file)
13 | defer configFile.Close()
14 | if err != nil {
15 | return models.Config{}, err
16 | }
17 | jsonParser := json.NewDecoder(configFile)
18 | jsonParser.Decode(&config)
19 | return config, nil
20 | }
21 |
22 | //GetConfig returns config object
23 | func GetConfig() models.Config {
24 | // Use by default ./config.json
25 | config, err := loadConfiguration("./config.json")
26 | if err != nil {
27 | // If it doesn't exist, take environnement variables
28 | config = models.Config{
29 | URI: os.Getenv("MONGODB_URI"),
30 | Database: os.Getenv("DATABASE"),
31 | Secret: os.Getenv("SECRET"),
32 | GmailAddress: os.Getenv("GMAIL_ADDRESS"),
33 | GmailPassword: os.Getenv("GMAIL_PASSWORD"),
34 | StripeKey: os.Getenv("STRIPE_KEY"),
35 | BucketName: os.Getenv("BUCKET_NAME"),
36 | BucketPubURL: os.Getenv("BUCKET_PUB_URL"),
37 | }
38 | return config
39 | }
40 | return config
41 | }
42 |
--------------------------------------------------------------------------------
/handlers/client/articles.go:
--------------------------------------------------------------------------------
1 | package clienthandlers
2 |
3 | import (
4 | "net/http"
5 |
6 | "github.com/backpulse/core/database"
7 | "github.com/backpulse/core/utils"
8 | "github.com/gorilla/mux"
9 | )
10 |
11 | // GetArticle : return specific article
12 | func GetArticle(w http.ResponseWriter, r *http.Request) {
13 | vars := mux.Vars(r)
14 | id := vars["short_id"]
15 | article, err := database.GetArticleByShortID(id)
16 | if err != nil {
17 | utils.RespondWithJSON(w, http.StatusNotFound, "not_found", nil)
18 | return
19 | }
20 | utils.RespondWithJSON(w, http.StatusOK, "success", article)
21 | return
22 | }
23 |
24 | // GetArticles : return array of article
25 | func GetArticles(w http.ResponseWriter, r *http.Request) {
26 | vars := mux.Vars(r)
27 | name := vars["name"]
28 |
29 | site, err := database.GetSiteByName(name)
30 | if err != nil {
31 | utils.RespondWithJSON(w, http.StatusNotFound, "not_found", nil)
32 | return
33 | }
34 |
35 | articles, err := database.GetArticles(site.ID)
36 | if err != nil {
37 | utils.RespondWithJSON(w, http.StatusNotFound, "not_found", nil)
38 | return
39 | }
40 |
41 | utils.RespondWithJSON(w, http.StatusOK, "success", articles)
42 | return
43 | }
44 |
--------------------------------------------------------------------------------
/database/database.go:
--------------------------------------------------------------------------------
1 | package database
2 |
3 | import (
4 | "github.com/sirupsen/logrus"
5 | mgo "gopkg.in/mgo.v2"
6 | )
7 |
8 | // DB : Database holder
9 | var DB *mgo.Database
10 |
11 | const (
12 | usersCollection string = "users"
13 | emailVerificationsCollection string = "email_verifications"
14 | sitesCollection string = "sites"
15 | contactCollection string = "contact"
16 | aboutCollection string = "about"
17 | articlesCollection string = "articles"
18 | projectsCollection string = "projects"
19 | galleriesCollection string = "galleries"
20 | photosCollection string = "photos"
21 | videoGroupsCollection string = "videogroups"
22 | videosCollection string = "videos"
23 | filesCollection string = "files"
24 | albumsCollection string = "albums"
25 | tracksCollection string = "tracks"
26 | )
27 |
28 | //Connect : Connect to MongoDB
29 | func Connect(server string, database string) {
30 | session, err := mgo.Dial(server)
31 |
32 | if err != nil {
33 | logrus.Fatal("Database: ERROR", err)
34 | }
35 | DB = session.DB(database)
36 | logrus.Infoln("Database: OK")
37 | }
38 |
--------------------------------------------------------------------------------
/utils/users.go:
--------------------------------------------------------------------------------
1 | package utils
2 |
3 | import (
4 | "net/http"
5 | "time"
6 |
7 | "github.com/backpulse/core/models"
8 | jwt "github.com/dgrijalva/jwt-go"
9 | "gopkg.in/mgo.v2/bson"
10 | )
11 |
12 | //NewJWT : generates new jwt
13 | func NewJWT(user models.User, expire int) (string, error) {
14 | config := GetConfig()
15 |
16 | token := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{
17 | "email": user.Email,
18 | "id": user.ID.Hex(),
19 | "exp": time.Now().Add(time.Hour * time.Duration(expire)), /* Token expires in x hours */
20 | })
21 | /* Sign token and get string */
22 | tokenString, err := token.SignedString([]byte(config.Secret))
23 | if err != nil {
24 | return "", err
25 | }
26 | return tokenString, nil
27 | }
28 |
29 | //GetUserObjectID : return id of jwt as type object id
30 | func GetUserObjectID(r *http.Request) bson.ObjectId {
31 | decoded := GetDecodedJWT(r)
32 | id := decoded["id"].(string)
33 | return bson.ObjectIdHex(id)
34 | }
35 |
36 | // IsAuthorized : Check if a user is authorized to edit site
37 | func IsAuthorized(site models.Site, user models.User) bool {
38 | if site.OwnerID == user.ID {
39 | return true
40 | }
41 | for _, collaborator := range site.Collaborators {
42 | if collaborator.Email == user.Email {
43 | return true
44 | }
45 | }
46 | return false
47 | }
48 |
--------------------------------------------------------------------------------
/handlers/client/projects.go:
--------------------------------------------------------------------------------
1 | package clienthandlers
2 |
3 | import (
4 | "net/http"
5 |
6 | "github.com/backpulse/core/database"
7 | "github.com/backpulse/core/utils"
8 | "github.com/gorilla/mux"
9 | )
10 |
11 | // GetProjects return array of projects
12 | func GetProjects(w http.ResponseWriter, r *http.Request) {
13 | vars := mux.Vars(r)
14 | name := vars["name"]
15 |
16 | site, err := database.GetSiteByName(name)
17 | if err != nil {
18 | utils.RespondWithJSON(w, http.StatusNotFound, "not_found", nil)
19 | return
20 | }
21 |
22 | projects, err := database.GetProjects(site.ID)
23 | if err != nil {
24 | utils.RespondWithJSON(w, http.StatusNotFound, err.Error(), nil)
25 | return
26 | }
27 | utils.RespondWithJSON(w, http.StatusOK, "success", projects)
28 | return
29 | }
30 |
31 | // GetProject return specific project
32 | func GetProject(w http.ResponseWriter, r *http.Request) {
33 | vars := mux.Vars(r)
34 | name := vars["name"]
35 | id := vars["short_id"]
36 |
37 | _, err := database.GetSiteByName(name)
38 | if err != nil {
39 | utils.RespondWithJSON(w, http.StatusNotFound, "not_found", nil)
40 | return
41 | }
42 |
43 | project, err := database.GetProjectByShortID(id)
44 | if err != nil {
45 | utils.RespondWithJSON(w, http.StatusNotFound, err.Error(), nil)
46 | return
47 | }
48 | utils.RespondWithJSON(w, http.StatusOK, "success", project)
49 | return
50 | }
51 |
--------------------------------------------------------------------------------
/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "flag"
5 | "log"
6 | "net/http"
7 | "os"
8 |
9 | "github.com/backpulse/core/database"
10 | "github.com/backpulse/core/routes"
11 | "github.com/backpulse/core/utils"
12 | "github.com/joho/godotenv"
13 | "github.com/rs/cors"
14 | )
15 |
16 | var (
17 | envFile string
18 | )
19 |
20 | func init() {
21 | // env file and will load them into ENV for this process.
22 | // will not overload value that defined in ENV had present.
23 | flag.StringVar(&envFile, "env", ".env", "env config file")
24 | }
25 |
26 | func main() {
27 | flag.Parse()
28 |
29 | log.SetFlags(log.LstdFlags | log.Lshortfile)
30 | godotenv.Load(envFile)
31 | config := utils.GetConfig()
32 |
33 | database.Connect(config.URI, config.Database)
34 | utils.InitGoogleCloud()
35 | utils.InitStripe()
36 |
37 | r := routes.NewRouter()
38 | c := cors.New(cors.Options{
39 | AllowedOrigins: []string{"*"},
40 | AllowedHeaders: []string{"Access-Control-Allow-Origin", "origin", "X-Requested-With", "Authorization", "Content-Type", "Language"},
41 | AllowedMethods: []string{"DELETE", "POST", "GET", "PUT"},
42 | })
43 |
44 | handler := c.Handler(r)
45 |
46 | var port string
47 | if os.Getenv("PORT") == "" {
48 | port = ":8000"
49 | } else {
50 | port = ":" + os.Getenv("PORT")
51 | }
52 |
53 | err := http.ListenAndServe(port, handler)
54 | if err != nil {
55 | log.Fatal(err)
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/routes/admin/admin.go:
--------------------------------------------------------------------------------
1 | package admin
2 |
3 | import (
4 | "net/http"
5 |
6 | jwtmiddleware "github.com/auth0/go-jwt-middleware"
7 | "github.com/backpulse/core/database"
8 | "github.com/backpulse/core/utils"
9 | jwt "github.com/dgrijalva/jwt-go"
10 | "github.com/gorilla/mux"
11 | "github.com/urfave/negroni"
12 | )
13 |
14 | var jwtMiddleware *jwtmiddleware.JWTMiddleware
15 |
16 | // HandleAdmin : setup all admin routes
17 | func HandleAdmin(r *mux.Router) *mux.Router {
18 | config := utils.GetConfig()
19 |
20 | jwtMiddleware = jwtmiddleware.New(jwtmiddleware.Options{
21 | ValidationKeyGetter: func(token *jwt.Token) (interface{}, error) {
22 | return []byte(config.Secret), nil
23 | },
24 | SigningMethod: jwt.SigningMethodHS256,
25 | })
26 |
27 | handleUsers(r)
28 | handleSites(r)
29 | handleContact(r)
30 | handleConstants(r)
31 | handleProjects(r)
32 | handleAbout(r)
33 | handlePhotos(r)
34 | handleGalleries(r)
35 | handleArticles(r)
36 | handleVideos(r)
37 | handleVideoGroups(r)
38 | handleFiles(r)
39 | handleAlbums(r)
40 | handleTracks(r)
41 |
42 | return r
43 | }
44 |
45 | // ProtectedRoute : returns a JWT protected route handler
46 | func ProtectedRoute(next func(w http.ResponseWriter, r *http.Request)) *negroni.Negroni {
47 | return negroni.New(negroni.HandlerFunc(jwtMiddleware.HandlerWithNext), negroni.WrapFunc(func(w http.ResponseWriter, r *http.Request) {
48 | id := utils.GetUserObjectID(r)
49 | _, err := database.GetUserByID(id)
50 | if err != nil {
51 | utils.RespondWithJSON(w, http.StatusUnauthorized, "unauthorized", nil)
52 | return
53 | }
54 | next(w, r)
55 | }))
56 | }
57 |
--------------------------------------------------------------------------------
/handlers/client/albums.go:
--------------------------------------------------------------------------------
1 | package clienthandlers
2 |
3 | import (
4 | "net/http"
5 |
6 | "github.com/backpulse/core/database"
7 | "github.com/backpulse/core/utils"
8 | "github.com/gorilla/mux"
9 | )
10 |
11 | // GetAlbums : return array of album
12 | func GetAlbums(w http.ResponseWriter, r *http.Request) {
13 | vars := mux.Vars(r)
14 | name := vars["name"]
15 |
16 | site, err := database.GetSiteByName(name)
17 | if err != nil {
18 | utils.RespondWithJSON(w, http.StatusNotFound, "not_found", nil)
19 | return
20 | }
21 |
22 | albums, err := database.GetAlbums(site.ID)
23 | if err != nil {
24 | utils.RespondWithJSON(w, http.StatusInternalServerError, "error", nil)
25 | return
26 | }
27 |
28 | utils.RespondWithJSON(w, http.StatusOK, "success", albums)
29 | return
30 | }
31 |
32 | // GetAlbum : return specific album
33 | func GetAlbum(w http.ResponseWriter, r *http.Request) {
34 | vars := mux.Vars(r)
35 | id := vars["short_id"]
36 |
37 | album, err := database.GetAlbumByShortID(id)
38 | if err != nil {
39 | utils.RespondWithJSON(w, http.StatusInternalServerError, "error", nil)
40 | return
41 | }
42 |
43 | utils.RespondWithJSON(w, http.StatusOK, "success", album)
44 | return
45 | }
46 |
47 | // GetTrack : return specific track informations
48 | func GetTrack(w http.ResponseWriter, r *http.Request) {
49 | vars := mux.Vars(r)
50 | id := vars["short_id"]
51 |
52 | track, err := database.GetTrackByShortID(id)
53 | if err != nil {
54 | utils.RespondWithJSON(w, http.StatusInternalServerError, "error", nil)
55 | return
56 | }
57 |
58 | utils.RespondWithJSON(w, http.StatusOK, "success", track)
59 | return
60 | }
61 |
--------------------------------------------------------------------------------
/strings.json:
--------------------------------------------------------------------------------
1 | {
2 | "fr": {
3 | "ERROR": "Erreur",
4 | "NOT_FOUND": "Non trouvé",
5 | "USERNAME_ALREADY_EXISTS": "Ce nom d'utilisateur est déjà utilisé.",
6 | "USERNAME_TOO_SHORT": "Ce nom d'utilisateur est trop court.",
7 | "USERNAME_TOO_LONG": "Ce nom d'utilisateur est trop long.",
8 | "PASSWORD_TOO_SHORT": "Ce mot de passe est trop court.",
9 | "PASSWORD_TOO_LONG": "Ce mot de passe est trop long.",
10 | "NAME_TOO_LONG": "Le nom est trop long.",
11 | "EMAIL_INVALID_FORMAT": "Ce format d'adresse email est invalide.",
12 | "EMAIL_ALREADY_EXISTS": "Cette adresse email a déjà été atribuée.",
13 | "WRONG_LAST_PASSWORD": "Le mot de passe est éronné",
14 | "WRONG_CREDENTIALS": "L'identifiant ou le mot de passe est éronné.",
15 | "UNAUTHORIZED": "Non autorisé"
16 | },
17 | "en": {
18 | "ERROR": "Error",
19 | "NOT_FOUND": "Not found",
20 | "USERNAME_ALREADY_EXISTS": "This username is already taken.",
21 | "USERNAME_TOO_SHORT": "This is username is too short.",
22 | "USERNAME_TOO_LONG": "This username is too long.",
23 | "PASSWORD_TOO_SHORT": "This password is too short.",
24 | "PASSWORD_TOO_LONG": "This password is too long.",
25 | "NAME_TOO_LONG": "Name is too long.",
26 | "EMAIL_INVALID_FORMAT": "Invalid email format.",
27 | "EMAIL_ALREADY_EXISTS": "This email has already been assigned.",
28 | "WRONG_LAST_PASSWORD": "This is not the correct password",
29 | "WRONG_CREDENTIALS": "Username/Email or password is wrong.",
30 | "UNAUTHORIZED": "Unauthorized"
31 | }
32 | }
--------------------------------------------------------------------------------
/routes/admin/sites.go:
--------------------------------------------------------------------------------
1 | package admin
2 |
3 | import (
4 | adminHandlers "github.com/backpulse/core/handlers/admin"
5 | "github.com/gorilla/mux"
6 | )
7 |
8 | func handleSites(r *mux.Router) {
9 | /* Sites */
10 | r.Handle("/sites", ProtectedRoute(adminHandlers.GetSites)).Methods("GET")
11 | r.Handle("/sites/{name}/overview", ProtectedRoute(adminHandlers.GetOverview)).Methods("GET")
12 | r.Handle("/sites/{name}", ProtectedRoute(adminHandlers.GetSite)).Methods("GET")
13 | r.Handle("/sites/{name}", ProtectedRoute(adminHandlers.UpdateSite)).Methods("PUT")
14 | r.Handle("/sites/{name}", ProtectedRoute(adminHandlers.DeleteSite)).Methods("DELETE")
15 | r.Handle("/sites", ProtectedRoute(adminHandlers.CreateSite)).Methods("POST")
16 |
17 | /* Favorite */
18 | r.Handle("/sites/favorite/{name}", ProtectedRoute(adminHandlers.Favorite)).Methods("PUT")
19 |
20 | /* Modules */
21 | r.Handle("/sites/{name}/modules/{module}", ProtectedRoute(adminHandlers.AddModule)).Methods("POST")
22 | r.Handle("/sites/{name}/modules/{module}", ProtectedRoute(adminHandlers.RemoveModule)).Methods("DELETE")
23 | r.Handle("/sites/{name}/modules", ProtectedRoute(adminHandlers.GetSiteModules)).Methods("GET")
24 |
25 | /* Collaborators */
26 | r.Handle("/sites/{name}/collaborators", ProtectedRoute(adminHandlers.GetCollaborators)).Methods("GET")
27 | r.Handle("/sites/{name}/collaborators/{email}", ProtectedRoute(adminHandlers.AddCollaborator)).Methods("POST")
28 | r.Handle("/sites/{name}/collaborators/{email}", ProtectedRoute(adminHandlers.RemoveCollaborator)).Methods("DELETE")
29 |
30 | /* Transfer */
31 | r.Handle("/sites/{name}/transfer/{email}", ProtectedRoute(adminHandlers.TransferSite)).Methods("POST")
32 | }
33 |
--------------------------------------------------------------------------------
/handlers/client/videos.go:
--------------------------------------------------------------------------------
1 | package clienthandlers
2 |
3 | import (
4 | "net/http"
5 |
6 | "github.com/backpulse/core/database"
7 | "github.com/backpulse/core/utils"
8 | "github.com/gorilla/mux"
9 | "gopkg.in/mgo.v2/bson"
10 | )
11 |
12 | // GetVideoGroups : return array of video group
13 | func GetVideoGroups(w http.ResponseWriter, r *http.Request) {
14 | vars := mux.Vars(r)
15 | name := vars["name"]
16 |
17 | site, err := database.GetSiteByName(name)
18 | if err != nil {
19 | utils.RespondWithJSON(w, http.StatusNotFound, "not_found", nil)
20 | return
21 | }
22 |
23 | videogroups, err := database.GetVideoGroups(site.ID)
24 | if err != nil {
25 | utils.RespondWithJSON(w, http.StatusInternalServerError, "error", nil)
26 | return
27 | }
28 |
29 | utils.RespondWithJSON(w, http.StatusOK, "success", videogroups)
30 | return
31 | }
32 |
33 | // GetVideoGroup // return specific video group
34 | func GetVideoGroup(w http.ResponseWriter, r *http.Request) {
35 | vars := mux.Vars(r)
36 | id := vars["short_id"]
37 |
38 | videogroup, err := database.GetVideoGroupByShortID(id)
39 | if err != nil {
40 | utils.RespondWithJSON(w, http.StatusInternalServerError, "error", nil)
41 | return
42 | }
43 |
44 | utils.RespondWithJSON(w, http.StatusOK, "success", videogroup)
45 | return
46 | }
47 |
48 | // GetVideo : return specific video informations
49 | func GetVideo(w http.ResponseWriter, r *http.Request) {
50 | vars := mux.Vars(r)
51 | id := vars["id"]
52 |
53 | video, err := database.GetVideo(bson.ObjectIdHex(id))
54 | if err != nil {
55 | utils.RespondWithJSON(w, http.StatusInternalServerError, "error", nil)
56 | return
57 | }
58 |
59 | utils.RespondWithJSON(w, http.StatusOK, "success", video)
60 | return
61 | }
62 |
--------------------------------------------------------------------------------
/utils/response.go:
--------------------------------------------------------------------------------
1 | package utils
2 |
3 | import (
4 | "encoding/json"
5 | "net/http"
6 |
7 | "github.com/backpulse/core/models"
8 | gomail "gopkg.in/gomail.v2"
9 | )
10 |
11 | //Response Struct
12 | type Response struct {
13 | Status string `json:"status"`
14 | Code int `json:"code"`
15 | Message string `json:"message"`
16 | Payload interface{} `json:"payload"`
17 | }
18 |
19 | //RespondWithJSON : Respond with JSON
20 | func RespondWithJSON(w http.ResponseWriter, code int, message string, payload interface{}) {
21 | var status string
22 | if code >= 200 && code <= 299 {
23 | status = "success"
24 | } else {
25 | status = "error"
26 | }
27 |
28 | response, _ := json.MarshalIndent(Response{
29 | Status: status,
30 | Code: code,
31 | Message: message,
32 | Payload: payload,
33 | }, "", " ")
34 |
35 | w.Header().Set("Content-Type", "application/json")
36 | w.WriteHeader(code)
37 | w.Write(response)
38 | }
39 |
40 | //SendVerificationMail : send verification email to user
41 | func SendVerificationMail(email string, verification models.EmailVerification) error {
42 | config := GetConfig()
43 | m := gomail.NewMessage()
44 |
45 | m.SetHeader("From", "no-reply@backpulse.io")
46 | m.SetHeader("To", email)
47 |
48 | m.SetHeader("Subject", "Please verify your email address")
49 |
50 | link := "https://www.backpulse.io/verify/" + verification.ID.Hex()
51 | linkAsATag := "" + link + ""
52 |
53 | m.SetBody("text/html", `Please click the following link to confirm that `+email+` is your email address.
`+linkAsATag+`
Thanks for using Backpulse!`)
54 |
55 | d := gomail.NewDialer("smtp.gmail.com", 465, config.GmailAddress, config.GmailPassword)
56 |
57 | err := d.DialAndSend(m)
58 | return err
59 | }
60 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | BIN_TARGET = backpulse
2 |
3 | GO ?= go
4 | GO_ON ?= GO111MODULE=on go
5 | GO_OFF ?= GO111MODULE=off go
6 | GOFMT ?= gofmt "-s"
7 | PACKAGES ?= $(shell GO111MODULE=on $(GO) list ./...)
8 | VETPACKAGES ?= $(shell GO111MODULE=on $(GO) list ./...)
9 | GOFILES := $(shell find . -name "*.go" -type f)
10 |
11 | .PHONY: default
12 | default: build
13 |
14 | .PHONY: build
15 | build:
16 | $(GO_ON) mod download
17 | $(GO_ON) build -o $(BIN_TARGET) github.com/backpulse/core
18 |
19 | .PHONY: ci
20 | ci: misspell lint vet test
21 |
22 | .PHONY: test
23 | test: fmt
24 | $(GO_ON) test -race ./...
25 |
26 | .PHONY: fmt
27 | fmt:
28 | $(GOFMT) -w $(GOFILES)
29 |
30 | .PHONY: fmt-check
31 | fmt-check:
32 | @diff=$$($(GOFMT) -d $(GOFILES)); \
33 | if [ -n "$$diff" ]; then \
34 | echo "Please run 'make fmt' and commit the result:"; \
35 | echo "$${diff}"; \
36 | exit 1; \
37 | fi;
38 |
39 | .PHONY: vet
40 | vet:
41 | $(GO_ON) vet $(VETPACKAGES)
42 |
43 | .PHONY: lint
44 | lint:
45 | @hash golint > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
46 | $(GO_OFF) get -u golang.org/x/lint/golint; \
47 | fi
48 | for PKG in $(PACKAGES); do golint -min_confidence 1.0 -set_exit_status $$PKG || exit 1; done;
49 |
50 | .PHONY: misspell-check
51 | misspell-check:
52 | @hash misspell > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
53 | $(GO_OFF) get -u github.com/client9/misspell/cmd/misspell; \
54 | fi
55 | misspell -error $(GOFILES)
56 |
57 | .PHONY: misspell
58 | misspell:
59 | @hash misspell > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
60 | $(GO_OFF) get -u github.com/client9/misspell/cmd/misspell; \
61 | fi
62 | misspell -w $(GOFILES)
63 |
64 | .PHONY: tools
65 | tools:
66 | $(GO_OFF) get golang.org/x/lint/golint
67 | $(GO_OFF) get github.com/client9/misspell/cmd/misspell
68 |
69 | .PHONY: clean
70 | clean:
71 | $(GO_ON) clean -r ./...
72 | -rm -f $(BIN_TARGET)
73 |
--------------------------------------------------------------------------------
/database/articles.go:
--------------------------------------------------------------------------------
1 | package database
2 |
3 | import (
4 | "log"
5 | "time"
6 |
7 | "github.com/backpulse/core/models"
8 | "github.com/teris-io/shortid"
9 | "gopkg.in/mgo.v2/bson"
10 | )
11 |
12 | // GetArticles : return array of articles of site
13 | func GetArticles(id bson.ObjectId) ([]models.Article, error) {
14 | var articles []models.Article
15 | err := DB.C(articlesCollection).Find(bson.M{
16 | "site_id": id,
17 | }).All(&articles)
18 | return articles, err
19 | }
20 |
21 | // RemoveArticle : remove article from db
22 | func RemoveArticle(id bson.ObjectId) error {
23 | err := DB.C(articlesCollection).RemoveId(id)
24 | return err
25 | }
26 |
27 | // GetArticle : return single article based by short_id
28 | func GetArticleByShortID(shortID string) (models.Article, error) {
29 | var article models.Article
30 | err := DB.C(articlesCollection).Find(bson.M{
31 | "short_id": shortID,
32 | }).One(&article)
33 | return article, err
34 | }
35 |
36 | // GetArticle : return single article based by short_id
37 | func GetArticle(id bson.ObjectId) (models.Article, error) {
38 | var article models.Article
39 | err := DB.C(articlesCollection).FindId(id).One(&article)
40 | return article, err
41 | }
42 |
43 | // UpsertArticle : create/update article
44 | func UpsertArticle(article models.Article) (models.Article, error) {
45 | article.UpdatedAt = time.Now()
46 | if article.ID == "" {
47 | article.CreatedAt = time.Now()
48 | article.ID = bson.NewObjectId()
49 | article.ShortID, _ = shortid.Generate()
50 | err := DB.C(articlesCollection).Insert(article)
51 | log.Println(err)
52 | return article, err
53 | }
54 | err := DB.C(articlesCollection).UpdateId(article.ID, bson.M{
55 | "$set": bson.M{
56 | "title": article.Title,
57 | "content": article.Content,
58 | "updated_at": article.UpdatedAt,
59 | },
60 | })
61 | log.Println(err)
62 |
63 | return article, err
64 | }
65 |
--------------------------------------------------------------------------------
/database/videos.go:
--------------------------------------------------------------------------------
1 | package database
2 |
3 | import (
4 | "time"
5 |
6 | "github.com/backpulse/core/models"
7 | "gopkg.in/mgo.v2/bson"
8 | )
9 |
10 | // AddVideo : add video to db
11 | func AddVideo(video models.Video) error {
12 | video.UpdatedAt = time.Now()
13 | video.CreatedAt = time.Now()
14 | err := DB.C(videosCollection).Insert(video)
15 | return err
16 | }
17 |
18 | // GetVideo : Return specific video by ObjectID
19 | func GetVideo(videoID bson.ObjectId) (models.Video, error) {
20 | var video models.Video
21 | err := DB.C(videosCollection).FindId(videoID).One(&video)
22 | return video, err
23 | }
24 |
25 | // GetGroupVideos : return array of video from a videogroup
26 | func GetGroupVideos(id bson.ObjectId) ([]models.Video, error) {
27 | var videos []models.Video
28 | err := DB.C(videosCollection).Find(bson.M{
29 | "video_group_id": id,
30 | }).All(&videos)
31 | return videos, err
32 | }
33 |
34 | // Updatevideo : update video informations (title, content, youtube_url)
35 | func UpdateVideo(id bson.ObjectId, video models.Video) error {
36 | err := DB.C(videosCollection).UpdateId(id, bson.M{
37 | "$set": bson.M{
38 | "title": video.Title,
39 | "content": video.Content,
40 | "youtube_url": video.YouTubeURL,
41 | },
42 | })
43 | return err
44 | }
45 |
46 | // RemoveVideo : remove video from db
47 | func RemoveVideo(id bson.ObjectId) error {
48 | err := DB.C(videosCollection).RemoveId(id)
49 | return err
50 | }
51 |
52 | // UpdateVideosIndexes : update order of videos
53 | func UpdateVideosIndexes(siteID bson.ObjectId, videos []models.Video) error {
54 | for _, video := range videos {
55 | err := DB.C(videosCollection).Update(bson.M{
56 | "site_id": siteID,
57 | "_id": video.ID,
58 | }, bson.M{
59 | "$set": bson.M{
60 | "index": video.Index,
61 | },
62 | })
63 | if err != nil {
64 | return err
65 | }
66 | }
67 | return nil
68 | }
69 |
--------------------------------------------------------------------------------
/handlers/admin/about.go:
--------------------------------------------------------------------------------
1 | package adminhandlers
2 |
3 | import (
4 | "encoding/json"
5 | "net/http"
6 |
7 | "github.com/backpulse/core/database"
8 | "github.com/backpulse/core/models"
9 | "github.com/backpulse/core/utils"
10 | "github.com/gorilla/mux"
11 | )
12 |
13 | // GetAbout : return about content of site
14 | func GetAbout(w http.ResponseWriter, r *http.Request) {
15 | vars := mux.Vars(r)
16 | name := vars["name"]
17 |
18 | site, _ := database.GetSiteByName(name)
19 | user, _ := database.GetUserByID(utils.GetUserObjectID(r))
20 |
21 | if !utils.IsAuthorized(site, user) {
22 | utils.RespondWithJSON(w, http.StatusUnauthorized, "unauthorized", nil)
23 | return
24 | }
25 |
26 | aboutContent, err := database.GetAbout(site.ID)
27 |
28 | if err != nil {
29 | utils.RespondWithJSON(w, http.StatusNotFound, "not_found", nil)
30 | return
31 | }
32 | utils.RespondWithJSON(w, http.StatusOK, "success", aboutContent)
33 | return
34 | }
35 |
36 | // UpdateAbout : update about content of site
37 | func UpdateAbout(w http.ResponseWriter, r *http.Request) {
38 | vars := mux.Vars(r)
39 | name := vars["name"]
40 |
41 | var aboutContent models.AboutContent
42 | /* Parse json to models.User */
43 | err := json.NewDecoder(r.Body).Decode(&aboutContent)
44 | if err != nil {
45 | utils.RespondWithJSON(w, http.StatusNotAcceptable, "error", nil)
46 | return
47 | }
48 |
49 | /* Check correct owner */
50 | site, _ := database.GetSiteByName(name)
51 | user, _ := database.GetUserByID(utils.GetUserObjectID(r))
52 |
53 | if !utils.IsAuthorized(site, user) {
54 | utils.RespondWithJSON(w, http.StatusUnauthorized, "unauthorized", nil)
55 | return
56 | }
57 |
58 | if len(aboutContent.Name) > 60 {
59 | utils.RespondWithJSON(w, http.StatusNotAcceptable, "name_too_long", nil)
60 | return
61 | }
62 |
63 | aboutContent.SiteID = site.ID
64 | aboutContent.OwnerID = site.OwnerID
65 |
66 | database.UpdateAbout(site.ID, aboutContent)
67 | utils.RespondWithJSON(w, http.StatusOK, "success", nil)
68 | return
69 | }
70 |
--------------------------------------------------------------------------------
/routes/client/client.go:
--------------------------------------------------------------------------------
1 | package client
2 |
3 | import (
4 | "net/http"
5 |
6 | clientHandlers "github.com/backpulse/core/handlers/client"
7 | "github.com/backpulse/core/utils"
8 | "github.com/gorilla/mux"
9 | "gopkg.in/mgo.v2/bson"
10 | )
11 |
12 | // HandleClient : handle client routes
13 | func HandleClient(r *mux.Router) {
14 |
15 | greetings := func(w http.ResponseWriter, r *http.Request) {
16 | utils.RespondWithJSON(w, http.StatusOK, "Welcome to the API", bson.M{
17 | "wrapper": "https://github.com/backpulse/wrapper",
18 | })
19 | }
20 |
21 | r.HandleFunc("", greetings).Methods("GET")
22 | r.HandleFunc("/", greetings).Methods("GET")
23 |
24 | r.HandleFunc("/contact", clientHandlers.GetContact).Methods("GET")
25 |
26 | r.HandleFunc("/about", clientHandlers.GetAbout).Methods("GET")
27 |
28 | r.HandleFunc("/galleries/default", clientHandlers.GetDefaultGallery).Methods("GET")
29 | r.HandleFunc("/galleries", clientHandlers.GetGalleries).Methods("GET")
30 | r.HandleFunc("/gallery/{short_id}", clientHandlers.GetGallery).Methods("GET")
31 |
32 | r.HandleFunc("/photos", clientHandlers.GetPhotos).Methods("GET")
33 | r.HandleFunc("/photos/{id}", clientHandlers.GetPhoto).Methods("GET")
34 |
35 | r.HandleFunc("/projects", clientHandlers.GetProjects).Methods("GET")
36 | r.HandleFunc("/projects/{short_id}", clientHandlers.GetProject).Methods("GET")
37 |
38 | r.HandleFunc("/articles", clientHandlers.GetArticles).Methods("GET")
39 | r.HandleFunc("/articles/{short_id}", clientHandlers.GetArticle).Methods("GET")
40 |
41 | r.HandleFunc("/videogroups", clientHandlers.GetVideoGroups).Methods("GET")
42 | r.HandleFunc("/videogroups/{short_id}", clientHandlers.GetVideoGroup).Methods("GET")
43 |
44 | r.HandleFunc("/videos/{short_id}", clientHandlers.GetVideo).Methods("GET")
45 |
46 | r.HandleFunc("/albums", clientHandlers.GetAlbums).Methods("GET")
47 | r.HandleFunc("/albums/{short_id}", clientHandlers.GetAlbum).Methods("GET")
48 |
49 | r.HandleFunc("/tracks/{short_id}", clientHandlers.GetTrack).Methods("GET")
50 |
51 | }
52 |
--------------------------------------------------------------------------------
/database/projects.go:
--------------------------------------------------------------------------------
1 | package database
2 |
3 | import (
4 | "time"
5 |
6 | "github.com/backpulse/core/models"
7 | "github.com/teris-io/shortid"
8 | "gopkg.in/mgo.v2/bson"
9 | )
10 |
11 | // GetProject : return project using shortid
12 | func GetProjectByShortID(shortID string) (models.Project, error) {
13 | var project models.Project
14 | err := DB.C(projectsCollection).Find(bson.M{
15 | "short_id": shortID,
16 | }).One(&project)
17 |
18 | project.Title = getDefaultProjectTitle(project)
19 |
20 | return project, err
21 | }
22 |
23 | // GetProject : return project using shortid
24 | func GetProject(id bson.ObjectId) (models.Project, error) {
25 | var project models.Project
26 | err := DB.C(projectsCollection).FindId(id).One(&project)
27 |
28 | project.Title = getDefaultProjectTitle(project)
29 |
30 | return project, err
31 | }
32 |
33 | // RemoveProject : remove project from db
34 | func RemoveProject(id bson.ObjectId) error {
35 | err := DB.C(projectsCollection).RemoveId(id)
36 | return err
37 | }
38 |
39 | // GetProjects : return projects of site
40 | func GetProjects(id bson.ObjectId) ([]models.Project, error) {
41 | var projects []models.Project
42 | err := DB.C(projectsCollection).Find(bson.M{
43 | "site_id": id,
44 | }).All(&projects)
45 |
46 | for i := range projects {
47 | title := getDefaultProjectTitle(projects[i])
48 | projects[i].Title = title
49 | }
50 |
51 | return projects, err
52 | }
53 |
54 | // UpsertProject Update or insert project
55 | func UpsertProject(project models.Project) error {
56 | if project.ID == "" {
57 | project.CreatedAt = time.Now()
58 | project.ID = bson.NewObjectId()
59 | project.ShortID, _ = shortid.Generate()
60 | }
61 | project.UpdatedAt = time.Now()
62 | _, err := DB.C(projectsCollection).UpsertId(project.ID, bson.M{
63 | "$set": project,
64 | })
65 | return err
66 | }
67 |
68 | // GetDefaultProjectTitle : return default project title
69 | func getDefaultProjectTitle(project models.Project) string {
70 | for i := range project.Titles {
71 | if project.Titles[i].LanguageCode == "en" {
72 | return project.Titles[i].Content
73 | }
74 | }
75 | return project.Titles[0].Content
76 | }
77 |
--------------------------------------------------------------------------------
/database/tracks.go:
--------------------------------------------------------------------------------
1 | package database
2 |
3 | import (
4 | "time"
5 |
6 | "github.com/backpulse/core/models"
7 | "gopkg.in/mgo.v2/bson"
8 | )
9 |
10 | // AddTrack : create track object in db
11 | func AddTrack(track models.Track) error {
12 | track.UpdatedAt = time.Now()
13 | track.CreatedAt = time.Now()
14 | err := DB.C(tracksCollection).Insert(track)
15 | return err
16 | }
17 |
18 | // GetTrack : return specific track by ObjectID
19 | func GetTrack(trackID bson.ObjectId) (models.Track, error) {
20 | var track models.Track
21 | err := DB.C(tracksCollection).FindId(trackID).One(&track)
22 | return track, err
23 | }
24 |
25 | // GetTrack : return specific track by shortID
26 | func GetTrackByShortID(shortID string) (models.Track, error) {
27 | var track models.Track
28 | err := DB.C(tracksCollection).Find(bson.M{
29 | "short_id": shortID,
30 | }).One(&track)
31 | return track, err
32 | }
33 |
34 | // GetAlbumTracks : return array of track for specific album
35 | func GetAlbumTracks(id bson.ObjectId) ([]models.Track, error) {
36 | var tracks []models.Track
37 | err := DB.C(tracksCollection).Find(bson.M{
38 | "album_id": id,
39 | }).All(&tracks)
40 | return tracks, err
41 | }
42 |
43 | // UpdateTrack : update track informations (title, url, image)
44 | func UpdateTrack(id bson.ObjectId, track models.Track) error {
45 | err := DB.C(tracksCollection).UpdateId(id, bson.M{
46 | "$set": bson.M{
47 | "title": track.Title,
48 | "url": track.URL,
49 | "image": track.Image,
50 | "content": track.Content,
51 | },
52 | })
53 | return err
54 | }
55 |
56 | // RemoveTrack : delete track from db
57 | func RemoveTrack(id bson.ObjectId) error {
58 | err := DB.C(tracksCollection).RemoveId(id)
59 | return err
60 | }
61 |
62 | // UpdateTracksIndexes : update order of tracks
63 | func UpdateTracksIndexes(siteID bson.ObjectId, tracks []models.Track) error {
64 | for _, track := range tracks {
65 | err := DB.C(tracksCollection).Update(bson.M{
66 | "site_id": siteID,
67 | "_id": track.ID,
68 | }, bson.M{
69 | "$set": bson.M{
70 | "index": track.Index,
71 | },
72 | })
73 | if err != nil {
74 | return err
75 | }
76 | }
77 | return nil
78 | }
79 |
--------------------------------------------------------------------------------
/database/albums.go:
--------------------------------------------------------------------------------
1 | package database
2 |
3 | import (
4 | "time"
5 |
6 | "github.com/backpulse/core/models"
7 | "gopkg.in/mgo.v2/bson"
8 | )
9 |
10 | // AddAlbum : create album object in db
11 | func AddAlbum(album models.Album) error {
12 | album.UpdatedAt = time.Now()
13 | album.CreatedAt = time.Now()
14 | err := DB.C(albumsCollection).Insert(album)
15 | return err
16 | }
17 |
18 | // UpdateAlbum : update album (title, cover, description)
19 | func UpdateAlbum(id bson.ObjectId, album models.Album) error {
20 | err := DB.C(albumsCollection).UpdateId(id, bson.M{
21 | "$set": bson.M{
22 | "title": album.Title,
23 | "cover": album.Cover,
24 | "description": album.Description,
25 | },
26 | })
27 | return err
28 | }
29 |
30 | // GetAlbum : return specific album by ObjectID
31 | func GetAlbum(ID bson.ObjectId) (models.Album, error) {
32 | var album models.Album
33 | err := DB.C(albumsCollection).FindId(ID).One(&album)
34 |
35 | tracks, err := GetAlbumTracks(album.ID)
36 | if err != nil {
37 | return models.Album{}, nil
38 | }
39 | album.Tracks = tracks
40 |
41 | return album, err
42 | }
43 |
44 | // GetAlbumByShortID : return specific album by short_id
45 | func GetAlbumByShortID(id string) (models.Album, error) {
46 | var album models.Album
47 | err := DB.C(albumsCollection).Find(bson.M{
48 | "short_id": id,
49 | }).One(&album)
50 |
51 | tracks, err := GetAlbumTracks(album.ID)
52 | if err != nil {
53 | return models.Album{}, nil
54 | }
55 | album.Tracks = tracks
56 |
57 | return album, err
58 | }
59 |
60 | // GetAlbums : return array of album for specific site
61 | func GetAlbums(siteID bson.ObjectId) ([]models.Album, error) {
62 | var albums []models.Album
63 | err := DB.C(albumsCollection).Find(bson.M{
64 | "site_id": siteID,
65 | }).All(&albums)
66 |
67 | return albums, err
68 | }
69 |
70 | // RemoveAlbum : delete album object from db
71 | func RemoveAlbum(id bson.ObjectId) error {
72 | err := DB.C(albumsCollection).RemoveId(id)
73 | return err
74 | }
75 |
76 | // UpdateAlbumsIndexes : change order of albums
77 | func UpdateAlbumsIndexes(siteID bson.ObjectId, albums []models.Album) error {
78 | for _, album := range albums {
79 | err := DB.C(albumsCollection).Update(bson.M{
80 | "site_id": siteID,
81 | "_id": album.ID,
82 | }, bson.M{
83 | "$set": bson.M{
84 | "index": album.Index,
85 | },
86 | })
87 | if err != nil {
88 | return err
89 | }
90 | }
91 | return nil
92 | }
93 |
--------------------------------------------------------------------------------
/database/videogroups.go:
--------------------------------------------------------------------------------
1 | package database
2 |
3 | import (
4 | "time"
5 |
6 | "github.com/backpulse/core/models"
7 | "gopkg.in/mgo.v2/bson"
8 | )
9 |
10 | // AddVideoGroup : add video group to db
11 | func AddVideoGroup(videoGroup models.VideoGroup) error {
12 | videoGroup.UpdatedAt = time.Now()
13 | videoGroup.CreatedAt = time.Now()
14 | err := DB.C(videoGroupsCollection).Insert(videoGroup)
15 | return err
16 | }
17 |
18 | // UpdateVideoGroup : update video group informations (title, image)
19 | func UpdateVideoGroup(id bson.ObjectId, group models.VideoGroup) error {
20 | err := DB.C(videoGroupsCollection).UpdateId(id, bson.M{
21 | "$set": bson.M{
22 | "title": group.Title,
23 | "image": group.Image,
24 | },
25 | })
26 | return err
27 | }
28 |
29 | // GetVideoGroup : return specific video group by ObjectID
30 | func GetVideoGroup(ID bson.ObjectId) (models.VideoGroup, error) {
31 | var videoGroup models.VideoGroup
32 | err := DB.C(videoGroupsCollection).FindId(ID).One(&videoGroup)
33 |
34 | videos, err := GetGroupVideos(videoGroup.ID)
35 | if err != nil {
36 | return models.VideoGroup{}, nil
37 | }
38 | videoGroup.Videos = videos
39 |
40 | return videoGroup, err
41 | }
42 |
43 | // GetVideoGroupByShortID : return specific video group by short_id
44 | func GetVideoGroupByShortID(id string) (models.VideoGroup, error) {
45 | var videoGroup models.VideoGroup
46 | err := DB.C(videoGroupsCollection).Find(bson.M{
47 | "short_id": id,
48 | }).One(&videoGroup)
49 |
50 | videos, err := GetGroupVideos(videoGroup.ID)
51 | if err != nil {
52 | return models.VideoGroup{}, nil
53 | }
54 | videoGroup.Videos = videos
55 |
56 | return videoGroup, err
57 | }
58 |
59 | // GetVideoGroups : Return array of videogroup from site
60 | func GetVideoGroups(siteID bson.ObjectId) ([]models.VideoGroup, error) {
61 | var videoGroups []models.VideoGroup
62 | err := DB.C(videoGroupsCollection).Find(bson.M{
63 | "site_id": siteID,
64 | }).All(&videoGroups)
65 |
66 | return videoGroups, err
67 | }
68 |
69 | // RemoveVideoGroup : remove video group from db
70 | func RemoveVideoGroup(id bson.ObjectId) error {
71 | err := DB.C(videoGroupsCollection).RemoveId(id)
72 | return err
73 | }
74 |
75 | // UpdateVideoGroupsIndexes : Update order of video groups
76 | func UpdateVideoGroupsIndexes(siteID bson.ObjectId, groups []models.VideoGroup) error {
77 | for _, g := range groups {
78 | err := DB.C(videoGroupsCollection).Update(bson.M{
79 | "site_id": siteID,
80 | "_id": g.ID,
81 | }, bson.M{
82 | "$set": bson.M{
83 | "index": g.Index,
84 | },
85 | })
86 | if err != nil {
87 | return err
88 | }
89 | }
90 | return nil
91 | }
92 |
--------------------------------------------------------------------------------
/handlers/client/galleries.go:
--------------------------------------------------------------------------------
1 | package clienthandlers
2 |
3 | import (
4 | "net/http"
5 |
6 | "gopkg.in/mgo.v2/bson"
7 |
8 | "github.com/backpulse/core/database"
9 | "github.com/backpulse/core/utils"
10 | "github.com/gorilla/mux"
11 | )
12 |
13 | // GetGallery : return specific gallery
14 | func GetGallery(w http.ResponseWriter, r *http.Request) {
15 | vars := mux.Vars(r)
16 | id := vars["short_id"]
17 | gallery, err := database.GetGalleryByShortID(id)
18 | if err != nil {
19 | utils.RespondWithJSON(w, http.StatusNotFound, "not_found", nil)
20 | return
21 | }
22 | utils.RespondWithJSON(w, http.StatusOK, "success", gallery)
23 | return
24 | }
25 |
26 | func GetPhotos(w http.ResponseWriter, r *http.Request) {
27 | vars := mux.Vars(r)
28 | name := vars["name"]
29 | site, err := database.GetSiteByName(name)
30 | if err != nil {
31 | utils.RespondWithJSON(w, http.StatusNotFound, "not_found", nil)
32 | return
33 | }
34 | photos, _ := database.GetSitePhotos(site.ID)
35 | utils.RespondWithJSON(w, http.StatusOK, "success", photos)
36 | return
37 | }
38 |
39 | func GetPhoto(w http.ResponseWriter, r *http.Request) {
40 | vars := mux.Vars(r)
41 | id := bson.ObjectIdHex(vars["id"])
42 | photo, err := database.GetPhotoByID(id)
43 | if err != nil {
44 | utils.RespondWithJSON(w, http.StatusNotFound, "not_found", nil)
45 | return
46 | }
47 | utils.RespondWithJSON(w, http.StatusOK, "success", photo)
48 | return
49 | }
50 |
51 | // GetGalleries : return array of galleries
52 | func GetGalleries(w http.ResponseWriter, r *http.Request) {
53 | vars := mux.Vars(r)
54 | name := vars["name"]
55 |
56 | site, err := database.GetSiteByName(name)
57 | if err != nil {
58 | utils.RespondWithJSON(w, http.StatusNotFound, "not_found", nil)
59 | return
60 | }
61 |
62 | galleries, err := database.GetGalleries(site.ID)
63 | if err != nil {
64 | utils.RespondWithJSON(w, http.StatusNotFound, "not_found", nil)
65 | return
66 | }
67 |
68 | for i := range galleries {
69 | photos, _ := database.GetGalleryPhotos(galleries[i].ID)
70 | galleries[i].Photos = photos
71 | }
72 |
73 | utils.RespondWithJSON(w, http.StatusOK, "success", galleries)
74 | return
75 | }
76 |
77 | // GetHomeGallery : return home gallery of site
78 | func GetDefaultGallery(w http.ResponseWriter, r *http.Request) {
79 | vars := mux.Vars(r)
80 | name := vars["name"]
81 | site, err := database.GetSiteByName(name)
82 | if err != nil {
83 | utils.RespondWithJSON(w, http.StatusNotFound, "not_found", nil)
84 | return
85 | }
86 | gallery, err := database.GetDefaultGallery(site.ID)
87 | if err != nil {
88 | utils.RespondWithJSON(w, http.StatusNotFound, "not_found", nil)
89 | return
90 | }
91 | utils.RespondWithJSON(w, http.StatusOK, "success", gallery)
92 | return
93 |
94 | }
95 |
--------------------------------------------------------------------------------
/handlers/admin/contact.go:
--------------------------------------------------------------------------------
1 | package adminhandlers
2 |
3 | import (
4 | "encoding/json"
5 | "log"
6 | "net/http"
7 |
8 | "github.com/backpulse/core/database"
9 | "github.com/backpulse/core/models"
10 | "github.com/backpulse/core/utils"
11 | "github.com/gorilla/mux"
12 | )
13 |
14 | // GetContact : return contact data about site
15 | func GetContact(w http.ResponseWriter, r *http.Request) {
16 | vars := mux.Vars(r)
17 | name := vars["name"]
18 |
19 | site, _ := database.GetSiteByName(name)
20 | user, _ := database.GetUserByID(utils.GetUserObjectID(r))
21 |
22 | if !utils.IsAuthorized(site, user) {
23 | utils.RespondWithJSON(w, http.StatusUnauthorized, "unauthorized", nil)
24 | return
25 | }
26 |
27 | contact, err := database.GetContact(site.ID)
28 | if err != nil {
29 | log.Println(err)
30 | utils.RespondWithJSON(w, http.StatusNotAcceptable, "error", nil)
31 | return
32 | }
33 | utils.RespondWithJSON(w, http.StatusOK, "success", contact)
34 | return
35 | }
36 |
37 | // UpdateContact : update contact data
38 | func UpdateContact(w http.ResponseWriter, r *http.Request) {
39 | vars := mux.Vars(r)
40 | name := vars["name"]
41 |
42 | var contactContent models.ContactContent
43 | /* Parse json to models.User */
44 | err := json.NewDecoder(r.Body).Decode(&contactContent)
45 | if err != nil {
46 | utils.RespondWithJSON(w, http.StatusNotAcceptable, "error", nil)
47 | return
48 | }
49 |
50 | /* Check correct owner */
51 | site, _ := database.GetSiteByName(name)
52 | user, _ := database.GetUserByID(utils.GetUserObjectID(r))
53 |
54 | if !utils.IsAuthorized(site, user) {
55 | utils.RespondWithJSON(w, http.StatusUnauthorized, "unauthorized", nil)
56 | return
57 | }
58 |
59 | if len(contactContent.Name) > 60 {
60 | utils.RespondWithJSON(w, http.StatusNotAcceptable, "name_too_long", nil)
61 | return
62 | }
63 | if len(contactContent.Phone) > 25 {
64 | utils.RespondWithJSON(w, http.StatusNotAcceptable, "phone_too_long", nil)
65 | return
66 | }
67 | if len(contactContent.Email) > 100 {
68 | utils.RespondWithJSON(w, http.StatusNotAcceptable, "email_too_long", nil)
69 | return
70 | }
71 | if len(contactContent.Address) > 150 {
72 | utils.RespondWithJSON(w, http.StatusNotAcceptable, "address_too_long", nil)
73 | return
74 | }
75 | if len(contactContent.FacebookURL) > 125 {
76 | utils.RespondWithJSON(w, http.StatusNotAcceptable, "facebook_url_too_long", nil)
77 | return
78 | }
79 | if len(contactContent.InstagramURL) > 125 {
80 | utils.RespondWithJSON(w, http.StatusNotAcceptable, "instagram_url_too_long", nil)
81 | return
82 | }
83 | if len(contactContent.TwitterURL) > 125 {
84 | utils.RespondWithJSON(w, http.StatusNotAcceptable, "twitter_url_too_long", nil)
85 | return
86 | }
87 |
88 | contactContent.SiteID = site.ID
89 | contactContent.OwnerID = site.OwnerID
90 |
91 | database.UpdateContact(site.ID, contactContent)
92 | utils.RespondWithJSON(w, http.StatusOK, "success", nil)
93 |
94 | return
95 |
96 | }
97 |
--------------------------------------------------------------------------------
/database/photos.go:
--------------------------------------------------------------------------------
1 | package database
2 |
3 | import (
4 | "log"
5 | "time"
6 |
7 | "github.com/backpulse/core/models"
8 | "gopkg.in/mgo.v2/bson"
9 | )
10 |
11 | // InsertPhoto : insert photo in db
12 | func InsertPhoto(photo models.Photo) (models.Photo, error) {
13 | err := DB.C(photosCollection).Insert(photo)
14 | return photo, err
15 | }
16 |
17 | // GetGalleryPhotos return photos of gallery
18 | func GetGalleryPhotos(id bson.ObjectId) ([]models.Photo, error) {
19 | var photos []models.Photo
20 | err := DB.C(photosCollection).Find(bson.M{
21 | // "is_gallery": true,
22 | "gallery_id": id,
23 | }).All(&photos)
24 | return photos, err
25 | }
26 |
27 | // DeletePhotos : delete multiple photos from db
28 | func DeletePhotos(userID bson.ObjectId, ids []bson.ObjectId) error {
29 | log.Println(ids)
30 | _, err := DB.C(photosCollection).RemoveAll(bson.M{
31 | "owner_id": userID,
32 | "_id": bson.M{
33 | "$in": ids,
34 | },
35 | })
36 | return err
37 | }
38 |
39 | func GetPhotoByID(id bson.ObjectId) (models.Photo, error) {
40 | var photo models.Photo
41 | err := DB.C(photosCollection).FindId(id).One(&photo)
42 | return photo, err
43 | }
44 |
45 | // GetPhotos : return array of photos
46 | func GetPhotos(userID bson.ObjectId, ids []bson.ObjectId) ([]models.Photo, error) {
47 | var photos []models.Photo
48 | err := DB.C(photosCollection).Find(bson.M{
49 | "owner_id": userID,
50 | "_id": bson.M{
51 | "$in": ids,
52 | },
53 | }).All(&photos)
54 | return photos, err
55 | }
56 |
57 | // GetSitePhotos : return photos from site
58 | func GetSitePhotos(id bson.ObjectId) ([]models.Photo, error) {
59 | var photos []models.Photo
60 | err := DB.C(photosCollection).Find(bson.M{
61 | "site_id": id,
62 | }).All(&photos)
63 | return photos, err
64 | }
65 |
66 | // UpdatePhotosIndexes : update order of photos
67 | func UpdatePhotosIndexes(gallery models.Gallery, photos []models.Photo) error {
68 | for _, photo := range photos {
69 | err := DB.C(photosCollection).Update(bson.M{
70 | "site_id": gallery.SiteID,
71 | "_id": photo.ID,
72 | }, bson.M{
73 | "$set": bson.M{
74 | "index": photo.Index,
75 | },
76 | })
77 | if err != nil {
78 | return err
79 | }
80 | }
81 | return nil
82 | }
83 |
84 | // UpdatePhoto updates title & content
85 | func UpdatePhoto(id bson.ObjectId, photo models.Photo) error {
86 | update := bson.M{
87 | "title": photo.Title,
88 | "content": photo.Content,
89 | "gallery_id": photo.GalleryID,
90 | "url": photo.URL,
91 | }
92 | err := DB.C(photosCollection).UpdateId(id, bson.M{
93 | "$set": update,
94 | })
95 | return err
96 | }
97 |
98 | func CreatePhoto(photo models.Photo) (models.Photo, error) {
99 | photo.ID = bson.NewObjectId()
100 | photo.CreatedAt = time.Now()
101 | err := DB.C(photosCollection).Insert(photo)
102 | return photo, err
103 | }
104 |
105 | func UpdatePhotoURL(id bson.ObjectId, url string) error {
106 | err := DB.C(photosCollection).UpdateId(id, bson.M{
107 | "$set": bson.M{
108 | "url": url,
109 | },
110 | })
111 | return err
112 | }
113 |
--------------------------------------------------------------------------------
/utils/googlecloud.go:
--------------------------------------------------------------------------------
1 | package utils
2 |
3 | import (
4 | "context"
5 | "io"
6 | "log"
7 | "mime/multipart"
8 | "os"
9 |
10 | "cloud.google.com/go/storage"
11 | "github.com/backpulse/core/models"
12 | "github.com/sirupsen/logrus"
13 | "google.golang.org/api/option"
14 | "gopkg.in/mgo.v2/bson"
15 | )
16 |
17 | var ctx context.Context
18 | var gClient *storage.Client
19 |
20 | func InitGoogleCloud() {
21 | ctx = context.Background()
22 | c, err := GetGoogleCloudClient(ctx)
23 | if err != nil {
24 | logrus.Infoln("Google Cloud: ERROR", err)
25 | }
26 | gClient = c
27 | logrus.Infoln("Google Cloud: OK")
28 | return
29 | }
30 |
31 | // GetGoogleCloudClient : Return google cloud client
32 | func GetGoogleCloudClient(ctx context.Context) (*storage.Client, error) {
33 |
34 | // Use ./google_credentials.json by default
35 | if os.Getenv("GOOGLE_APPLICATION_CREDENTIALS") == "" {
36 | os.Setenv("GOOGLE_APPLICATION_CREDENTIALS", "./google_credentials.json")
37 | client, err := storage.NewClient(ctx)
38 | return client, err
39 | }
40 |
41 | // Use env variables
42 | client, err := storage.NewClient(ctx, option.WithCredentialsJSON([]byte(os.Getenv("GOOGLE_APPLICATION_CREDENTIALS"))))
43 | return client, err
44 | }
45 |
46 | // UpdateFilename : update filename of a file
47 | func UpdateFilename(fileID bson.ObjectId, filename string) error {
48 | config := GetConfig()
49 | bucketName := config.BucketName
50 |
51 | ctx := context.Background()
52 |
53 | bucket := gClient.Bucket(bucketName)
54 | object := bucket.Object(fileID.Hex())
55 |
56 | log.Print(fileID.Hex())
57 | _, err := object.Update(ctx, storage.ObjectAttrsToUpdate{
58 | ContentDisposition: "inline; filename=\"" + filename + "\"",
59 | })
60 | log.Print(err)
61 | return err
62 | }
63 |
64 | // UploadFile : upload file to google cloud
65 | func UploadFile(file multipart.File, fileName string) (bson.ObjectId, error) {
66 |
67 | config := GetConfig()
68 | bucketName := config.BucketName
69 |
70 | ctx := context.Background()
71 |
72 | objectID := bson.NewObjectId()
73 |
74 | bucket := gClient.Bucket(bucketName)
75 | object := bucket.Object(objectID.Hex())
76 |
77 | object.ACL().Set(ctx, storage.AllUsers, storage.RoleReader)
78 |
79 | wc := object.NewWriter(ctx)
80 | _, err := io.Copy(wc, file)
81 | if err != nil {
82 | log.Println(err)
83 | return "", err
84 | }
85 |
86 | err = wc.Close()
87 | if err != nil {
88 | log.Println(err)
89 | return "", err
90 | }
91 | update, err := object.Update(ctx, storage.ObjectAttrsToUpdate{
92 | ContentDisposition: "inline; filename=\"" + fileName + "\"",
93 | })
94 | log.Println("update", update)
95 | log.Println("error", err)
96 |
97 | return objectID, nil
98 | }
99 |
100 | // RemoveGoogleCloudPhotos : remove photos from google cloud to save space
101 | func RemoveGoogleCloudPhotos(photos []models.Photo) error {
102 | config := GetConfig()
103 | bucketName := config.BucketName
104 |
105 | ctx := context.Background()
106 |
107 | bucket := gClient.Bucket(bucketName)
108 | for _, photo := range photos {
109 | log.Println(photo.ID.Hex())
110 | object := bucket.Object(photo.ID.Hex())
111 | err := object.Delete(ctx)
112 | if err != nil {
113 | log.Println(err)
114 |
115 | return err
116 | }
117 | }
118 | return nil
119 | }
120 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | [](https://forthebadge.com)
2 | [](https://forthebadge.com)
3 |
4 | [](https://commerce.coinbase.com/checkout/b4d64264-dda8-41d0-9f15-0843f969fa79)
5 | [](https://www.patreon.com/backpulse)
6 | [](https://www.paypal.me/aureleoules)
7 |
8 | 
9 |
10 | # Backpulse core
11 | Backpulse is an API Based / Headless CMS.
12 | Your site's content is accessible directly via our RESTful API, on any web framework and any device.
13 |
14 | ## Installation
15 | With a correctly configured Go toolchain:
16 | ```bash
17 | go get github.com/backpulse/core
18 | ```
19 |
20 | ## Build&Run from source
21 | With a correctly configured(go version >=go1.11) Go toolchain:
22 | ```bash
23 | git clone https://github.com/backpulse/core
24 | cd core
25 | make build
26 | ./backpulse
27 | ```
28 |
29 | ## Docker Build&Run
30 | ```bash
31 | docker build -t .
32 | docker run -d --link :mongodb
33 | ```
34 | or docker run in custom environment
35 | ```bash
36 | docker run -d \
37 | --link :mongodb \
38 | --env MONGODB_URI=mongodb://mongodb:27017 \
39 | --env DATABASE=backpulse \
40 |
41 | ```
42 |
43 | ## Usage
44 | First, you need to create a config.json using the `config.json.template` file.
45 | * **URI** : MongoDB server address (_mongodb://..._)
46 | * **Database** : MongoDB database name
47 | * **Secret** : A secret key to encrypt JWT
48 | * **GmailAddress** : A gmail address if you wish to send confirmation emails
49 | * **GmailPassword** : The password associated with the gmail address obviously
50 | * **StripeKey** : Your Stripe Key if you wish to integrate Stripe
51 | * **BucketName** : Your Google Cloud Storage Bucket's name to store user files (images, binaries, plain text...)
52 |
53 | You can also pass all these variables as environment variables:
54 | * MONGODB_URI
55 | * DATABASE
56 | * SECRET
57 | * GMAIL_ADDRESS
58 | * GMAIL_PASSWORD
59 | * STRIPE_KEY
60 | * BUCKET_NAME
61 |
62 |
63 | **Note**: If a `config.json` file is found, it will override environment variables.
64 |
65 | Then, you need to get your Google Service Account Key:
66 | * Go to this [page](https://console.cloud.google.com/apis/credentials/serviceaccountkey).
67 | * Create a new account with the Project -> Owner role.
68 | * Download your private key as JSON.
69 | * Move it to the root of this project.
70 | * Rename it `google_credentials.json`.
71 |
72 | You can also pass the content of this json file as an environment variable:
73 |
74 | GOOGLE_APPLICATION_CREDENTIALS = `{"type": "service_account", "project_id": "projectID", ...}`
75 |
76 | You're all set to run **Backpulse**!
77 | ```bash
78 | go build -o backpulse && backpulse
79 | ```
80 |
81 | **Note**: By default Backpulse runs on port 8000, but can be overridden with the `PORT` environment variable.
82 |
83 | ## Contributing
84 | Pull requests are welcome. For major changes, please open an issue first to discuss what you would like to change.
85 |
86 |
87 | ## License
88 | [MIT](https://github.com/backpulse/core/blob/master/LICENSE) © [Aurèle Oulès](https://www.aureleoules.com)
--------------------------------------------------------------------------------
/CODE_OF_CONDUCT.md:
--------------------------------------------------------------------------------
1 | # Contributor Covenant Code of Conduct
2 |
3 | ## Our Pledge
4 |
5 | In the interest of fostering an open and welcoming environment, we as
6 | contributors and maintainers pledge to making participation in our project and
7 | our community a harassment-free experience for everyone, regardless of age, body
8 | size, disability, ethnicity, sex characteristics, gender identity and expression,
9 | level of experience, education, socio-economic status, nationality, personal
10 | appearance, race, religion, or sexual identity and orientation.
11 |
12 | ## Our Standards
13 |
14 | Examples of behavior that contributes to creating a positive environment
15 | include:
16 |
17 | * Using welcoming and inclusive language
18 | * Being respectful of differing viewpoints and experiences
19 | * Gracefully accepting constructive criticism
20 | * Focusing on what is best for the community
21 | * Showing empathy towards other community members
22 |
23 | Examples of unacceptable behavior by participants include:
24 |
25 | * The use of sexualized language or imagery and unwelcome sexual attention or
26 | advances
27 | * Trolling, insulting/derogatory comments, and personal or political attacks
28 | * Public or private harassment
29 | * Publishing others' private information, such as a physical or electronic
30 | address, without explicit permission
31 | * Other conduct which could reasonably be considered inappropriate in a
32 | professional setting
33 |
34 | ## Our Responsibilities
35 |
36 | Project maintainers are responsible for clarifying the standards of acceptable
37 | behavior and are expected to take appropriate and fair corrective action in
38 | response to any instances of unacceptable behavior.
39 |
40 | Project maintainers have the right and responsibility to remove, edit, or
41 | reject comments, commits, code, wiki edits, issues, and other contributions
42 | that are not aligned to this Code of Conduct, or to ban temporarily or
43 | permanently any contributor for other behaviors that they deem inappropriate,
44 | threatening, offensive, or harmful.
45 |
46 | ## Scope
47 |
48 | This Code of Conduct applies both within project spaces and in public spaces
49 | when an individual is representing the project or its community. Examples of
50 | representing a project or community include using an official project e-mail
51 | address, posting via an official social media account, or acting as an appointed
52 | representative at an online or offline event. Representation of a project may be
53 | further defined and clarified by project maintainers.
54 |
55 | ## Enforcement
56 |
57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be
58 | reported by contacting the project team at contact@backpulse.io. All
59 | complaints will be reviewed and investigated and will result in a response that
60 | is deemed necessary and appropriate to the circumstances. The project team is
61 | obligated to maintain confidentiality with regard to the reporter of an incident.
62 | Further details of specific enforcement policies may be posted separately.
63 |
64 | Project maintainers who do not follow or enforce the Code of Conduct in good
65 | faith may face temporary or permanent repercussions as determined by other
66 | members of the project's leadership.
67 |
68 | ## Attribution
69 |
70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
71 | available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
72 |
73 | [homepage]: https://www.contributor-covenant.org
74 |
75 | For answers to common questions about this code of conduct, see
76 | https://www.contributor-covenant.org/faq
77 |
--------------------------------------------------------------------------------
/handlers/admin/projects.go:
--------------------------------------------------------------------------------
1 | package adminhandlers
2 |
3 | import (
4 | "encoding/json"
5 | "log"
6 | "net/http"
7 |
8 | "github.com/backpulse/core/database"
9 | "github.com/backpulse/core/models"
10 | "github.com/backpulse/core/utils"
11 | "github.com/gorilla/mux"
12 | "gopkg.in/mgo.v2/bson"
13 | )
14 |
15 | // DeleteProject : remove project from db
16 | func DeleteProject(w http.ResponseWriter, r *http.Request) {
17 | vars := mux.Vars(r)
18 | id := vars["id"]
19 |
20 | project, _ := database.GetProject(bson.ObjectIdHex(id))
21 | site, _ := database.GetSiteByID(project.SiteID)
22 | user, _ := database.GetUserByID(utils.GetUserObjectID(r))
23 | if !utils.IsAuthorized(site, user) {
24 | utils.RespondWithJSON(w, http.StatusUnauthorized, "unauthorized", nil)
25 | return
26 | }
27 |
28 | if project.SiteID != site.ID {
29 | utils.RespondWithJSON(w, http.StatusUnauthorized, "unauthorized", nil)
30 | return
31 | }
32 |
33 | err := database.RemoveProject(bson.ObjectIdHex(id))
34 | if err != nil {
35 | utils.RespondWithJSON(w, http.StatusInternalServerError, "error", nil)
36 | return
37 | }
38 | utils.RespondWithJSON(w, http.StatusOK, "success", nil)
39 | return
40 | }
41 |
42 | // GetProject : return project using shortid
43 | func GetProject(w http.ResponseWriter, r *http.Request) {
44 | vars := mux.Vars(r)
45 | id := vars["id"]
46 |
47 | project, err := database.GetProject(bson.ObjectIdHex(id))
48 |
49 | site, _ := database.GetSiteByID(project.SiteID)
50 | user, _ := database.GetUserByID(utils.GetUserObjectID(r))
51 |
52 | if !utils.IsAuthorized(site, user) {
53 | utils.RespondWithJSON(w, http.StatusUnauthorized, "unauthorized", nil)
54 | return
55 | }
56 |
57 | if err != nil {
58 | utils.RespondWithJSON(w, http.StatusInternalServerError, "error", nil)
59 | return
60 | }
61 |
62 | if project.OwnerID != site.OwnerID {
63 | utils.RespondWithJSON(w, http.StatusUnauthorized, "unauthorized", nil)
64 | return
65 | }
66 |
67 | utils.RespondWithJSON(w, http.StatusOK, "success", project)
68 | return
69 | }
70 |
71 | // GetProjects : return array of projects of site
72 | func GetProjects(w http.ResponseWriter, r *http.Request) {
73 | vars := mux.Vars(r)
74 | name := vars["name"]
75 |
76 | site, _ := database.GetSiteByName(name)
77 | user, _ := database.GetUserByID(utils.GetUserObjectID(r))
78 | if !utils.IsAuthorized(site, user) {
79 | utils.RespondWithJSON(w, http.StatusUnauthorized, "unauthorized", nil)
80 | return
81 | }
82 |
83 | projects, err := database.GetProjects(site.ID)
84 | if err != nil {
85 | utils.RespondWithJSON(w, http.StatusInternalServerError, "error", nil)
86 | return
87 | }
88 |
89 | utils.RespondWithJSON(w, http.StatusOK, "success", projects)
90 | return
91 | }
92 |
93 | // UpdateProject : Update or insert new project
94 | func UpdateProject(w http.ResponseWriter, r *http.Request) {
95 | vars := mux.Vars(r)
96 | name := vars["name"]
97 |
98 | site, _ := database.GetSiteByName(name)
99 | user, _ := database.GetUserByID(utils.GetUserObjectID(r))
100 | if !utils.IsAuthorized(site, user) {
101 | utils.RespondWithJSON(w, http.StatusUnauthorized, "unauthorized", nil)
102 | return
103 | }
104 |
105 | var project models.Project
106 | /* Parse json to models.Project */
107 | err := json.NewDecoder(r.Body).Decode(&project)
108 | if err != nil {
109 | utils.RespondWithJSON(w, http.StatusNotAcceptable, "error", nil)
110 | return
111 | }
112 |
113 | if len(project.Titles) < 1 {
114 | utils.RespondWithJSON(w, http.StatusNotAcceptable, "title_required", nil)
115 | return
116 | }
117 |
118 | if len(project.URL) > 200 {
119 | utils.RespondWithJSON(w, http.StatusNotAcceptable, "url_too_long", nil)
120 | return
121 | }
122 |
123 | project.SiteID = site.ID
124 | project.OwnerID = site.OwnerID
125 | err = database.UpsertProject(project)
126 | if err != nil {
127 | log.Println(err)
128 | utils.RespondWithJSON(w, http.StatusInternalServerError, "error", nil)
129 | return
130 | }
131 |
132 | utils.RespondWithJSON(w, http.StatusOK, "success", nil)
133 | return
134 | }
135 |
--------------------------------------------------------------------------------
/handlers/admin/articles.go:
--------------------------------------------------------------------------------
1 | package adminhandlers
2 |
3 | import (
4 | "encoding/json"
5 | "log"
6 | "net/http"
7 |
8 | "github.com/backpulse/core/database"
9 | "github.com/backpulse/core/models"
10 | "github.com/backpulse/core/utils"
11 | "github.com/gorilla/mux"
12 | "gopkg.in/mgo.v2/bson"
13 | )
14 |
15 | // GetArticles : return array of article of site
16 | func GetArticles(w http.ResponseWriter, r *http.Request) {
17 | vars := mux.Vars(r)
18 | name := vars["name"]
19 |
20 | site, _ := database.GetSiteByName(name)
21 | user, _ := database.GetUserByID(utils.GetUserObjectID(r))
22 |
23 | if !utils.IsAuthorized(site, user) {
24 | utils.RespondWithJSON(w, http.StatusUnauthorized, "unauthorized", nil)
25 | return
26 | }
27 |
28 | articles, err := database.GetArticles(site.ID)
29 | if err != nil {
30 | utils.RespondWithJSON(w, http.StatusInternalServerError, "error", nil)
31 | return
32 | }
33 |
34 | utils.RespondWithJSON(w, http.StatusOK, "success", articles)
35 | return
36 | }
37 |
38 | // GetArticle : return specific article
39 | func GetArticle(w http.ResponseWriter, r *http.Request) {
40 | vars := mux.Vars(r)
41 | name := vars["name"]
42 | id := vars["id"]
43 |
44 | site, _ := database.GetSiteByName(name)
45 | user, _ := database.GetUserByID(utils.GetUserObjectID(r))
46 |
47 | if !utils.IsAuthorized(site, user) {
48 | utils.RespondWithJSON(w, http.StatusUnauthorized, "unauthorized", nil)
49 | return
50 | }
51 | article, err := database.GetArticle(bson.ObjectIdHex(id))
52 | if err != nil {
53 | utils.RespondWithJSON(w, http.StatusNotFound, "not_found", nil)
54 | return
55 | }
56 |
57 | if article.SiteID != site.ID {
58 | utils.RespondWithJSON(w, http.StatusUnauthorized, "unauthorized", nil)
59 | return
60 | }
61 |
62 | utils.RespondWithJSON(w, http.StatusOK, "success", article)
63 | return
64 |
65 | }
66 |
67 | // UpdateArticle : create/update article
68 | func UpdateArticle(w http.ResponseWriter, r *http.Request) {
69 | vars := mux.Vars(r)
70 | name := vars["name"]
71 |
72 | site, _ := database.GetSiteByName(name)
73 | user, _ := database.GetUserByID(utils.GetUserObjectID(r))
74 |
75 | if !utils.IsAuthorized(site, user) {
76 | utils.RespondWithJSON(w, http.StatusUnauthorized, "unauthorized", nil)
77 | return
78 | }
79 |
80 | var article models.Article
81 | /* Parse json to models.Project */
82 | err := json.NewDecoder(r.Body).Decode(&article)
83 | if err != nil {
84 | utils.RespondWithJSON(w, http.StatusNotAcceptable, "error", nil)
85 | return
86 | }
87 |
88 | if len(article.Title) < 1 {
89 | utils.RespondWithJSON(w, http.StatusNotAcceptable, "title_required", nil)
90 | return
91 | }
92 |
93 | if len(article.Title) > 200 {
94 | utils.RespondWithJSON(w, http.StatusNotAcceptable, "title_too_long", nil)
95 | return
96 | }
97 |
98 | if len(article.Content) < 1 {
99 | utils.RespondWithJSON(w, http.StatusNotAcceptable, "content_required", nil)
100 | return
101 | }
102 |
103 | if article.ID != "" {
104 | a, _ := database.GetArticle(article.ID)
105 | if a.SiteID != site.ID {
106 | utils.RespondWithJSON(w, http.StatusUnauthorized, "unauthorized", nil)
107 | return
108 | }
109 | }
110 |
111 | article.SiteID = site.ID
112 | article.OwnerID = site.OwnerID
113 | article, err = database.UpsertArticle(article)
114 | if err != nil {
115 | log.Println(err)
116 | utils.RespondWithJSON(w, http.StatusInternalServerError, "error", nil)
117 | return
118 | }
119 |
120 | utils.RespondWithJSON(w, http.StatusOK, "success", article)
121 | return
122 |
123 | }
124 |
125 | // DeleteArticle : remove article from db
126 | func DeleteArticle(w http.ResponseWriter, r *http.Request) {
127 | vars := mux.Vars(r)
128 | name := vars["name"]
129 | id := vars["id"]
130 |
131 | site, _ := database.GetSiteByName(name)
132 | user, _ := database.GetUserByID(utils.GetUserObjectID(r))
133 |
134 | if !utils.IsAuthorized(site, user) {
135 | utils.RespondWithJSON(w, http.StatusUnauthorized, "unauthorized", nil)
136 | return
137 | }
138 |
139 | article, err := database.GetArticle(bson.ObjectIdHex(id))
140 | if err != nil {
141 | utils.RespondWithJSON(w, http.StatusNotFound, "not_found", nil)
142 | return
143 | }
144 |
145 | if article.SiteID != site.ID {
146 | utils.RespondWithJSON(w, http.StatusUnauthorized, "unauthorized", nil)
147 | return
148 | }
149 |
150 | err = database.RemoveArticle(article.ID)
151 | if err != nil {
152 | utils.RespondWithJSON(w, http.StatusInternalServerError, "error", nil)
153 | return
154 | }
155 | utils.RespondWithJSON(w, http.StatusOK, "success", nil)
156 | return
157 | }
158 |
--------------------------------------------------------------------------------
/handlers/admin/files.go:
--------------------------------------------------------------------------------
1 | package adminhandlers
2 |
3 | import (
4 | "log"
5 | "math"
6 | "net/http"
7 | "strings"
8 | "time"
9 |
10 | "github.com/backpulse/core/database"
11 | "github.com/backpulse/core/models"
12 | "github.com/backpulse/core/utils"
13 | "github.com/gorilla/mux"
14 | "gopkg.in/mgo.v2/bson"
15 | )
16 |
17 | // GetFiles : Return array of files for site
18 | func GetFiles(w http.ResponseWriter, r *http.Request) {
19 | vars := mux.Vars(r)
20 | name := vars["name"]
21 |
22 | site, _ := database.GetSiteByName(name)
23 | user, _ := database.GetUserByID(utils.GetUserObjectID(r))
24 |
25 | if !utils.IsAuthorized(site, user) {
26 | utils.RespondWithJSON(w, http.StatusUnauthorized, "unauthorized", nil)
27 | return
28 | }
29 |
30 | files, err := database.GetSiteFiles(site.ID)
31 | if err != nil {
32 | utils.RespondWithJSON(w, http.StatusNotFound, "not_found", nil)
33 | return
34 | }
35 | utils.RespondWithJSON(w, http.StatusOK, "success", files)
36 | return
37 | }
38 |
39 | // UpdateFilename : update filename on gcloud
40 | func UpdateFilename(w http.ResponseWriter, r *http.Request) {
41 | vars := mux.Vars(r)
42 | name := vars["name"]
43 | id := vars["id"]
44 | filename := vars["filename"]
45 |
46 | site, _ := database.GetSiteByName(name)
47 | user, _ := database.GetUserByID(utils.GetUserObjectID(r))
48 |
49 | if !utils.IsAuthorized(site, user) {
50 | utils.RespondWithJSON(w, http.StatusUnauthorized, "unauthorized", nil)
51 | return
52 | }
53 |
54 | if len(filename) < 1 {
55 | utils.RespondWithJSON(w, http.StatusNotAcceptable, "name_required", nil)
56 | return
57 | }
58 |
59 | err := database.UpdateFilename(bson.ObjectIdHex(id), filename, site.ID)
60 | if err != nil {
61 | utils.RespondWithJSON(w, http.StatusNotFound, "not_found", nil)
62 | return
63 | }
64 |
65 | err = utils.UpdateFilename(bson.ObjectIdHex(id), filename)
66 | if err != nil {
67 | utils.RespondWithJSON(w, http.StatusInternalServerError, "error", nil)
68 | return
69 | }
70 |
71 | utils.RespondWithJSON(w, http.StatusOK, "success", nil)
72 | return
73 | }
74 |
75 | // UploadFile : upload file to gcloud
76 | func UploadFile(w http.ResponseWriter, r *http.Request) {
77 | /* Get file from Client */
78 | file, header, err := r.FormFile("file")
79 | if err != nil {
80 | utils.RespondWithJSON(w, http.StatusInternalServerError, err.Error(), nil)
81 | return
82 | }
83 | defer file.Close()
84 |
85 | vars := mux.Vars(r)
86 | name := vars["name"]
87 |
88 | site, _ := database.GetSiteByName(name)
89 | user, _ := database.GetUserByID(utils.GetUserObjectID(r))
90 |
91 | if !utils.IsAuthorized(site, user) {
92 | utils.RespondWithJSON(w, http.StatusUnauthorized, "unauthorized", nil)
93 | return
94 | }
95 |
96 | id, err := utils.UploadFile(file, header.Filename)
97 | if err != nil {
98 | log.Println(err)
99 | utils.RespondWithJSON(w, http.StatusInternalServerError, "error", nil)
100 | return
101 | }
102 |
103 | config := utils.GetConfig()
104 |
105 | fileObject := models.File{
106 | ID: id,
107 | Name: header.Filename,
108 | OwnerID: site.OwnerID,
109 | SiteID: site.ID,
110 | Size: math.Round(float64(header.Size)/10000) / 100,
111 | CreatedAt: time.Now(),
112 | Type: header.Header.Get("Content-Type"),
113 | URL: config.BucketPubURL + "/" + id.Hex(),
114 | }
115 |
116 | err = database.InsertFile(fileObject)
117 | if err != nil {
118 | utils.RespondWithJSON(w, http.StatusInternalServerError, "error", nil)
119 | return
120 | }
121 | utils.RespondWithJSON(w, http.StatusOK, "success", fileObject)
122 | return
123 | }
124 |
125 | // DeleteFiles : remove files from db & gcloud
126 | func DeleteFiles(w http.ResponseWriter, r *http.Request) {
127 | vars := mux.Vars(r)
128 | name := vars["name"]
129 | fileIds := vars["ids"]
130 |
131 | stringIDsArray := strings.Split(fileIds, ",")
132 | var ids []bson.ObjectId
133 |
134 | for _, id := range stringIDsArray {
135 | if bson.IsObjectIdHex(id) {
136 | ids = append(ids, bson.ObjectIdHex(id))
137 | }
138 | }
139 |
140 | site, _ := database.GetSiteByName(name)
141 | user, _ := database.GetUserByID(utils.GetUserObjectID(r))
142 |
143 | if !utils.IsAuthorized(site, user) {
144 | utils.RespondWithJSON(w, http.StatusUnauthorized, "unauthorized", nil)
145 | return
146 | }
147 |
148 | for _, id := range ids {
149 | err := database.DeleteFile(id, site.ID)
150 | if err != nil {
151 | log.Println(err)
152 | utils.RespondWithJSON(w, http.StatusNotFound, "not_found", nil)
153 | return
154 | }
155 | }
156 |
157 | utils.RespondWithJSON(w, http.StatusOK, "success", nil)
158 | return
159 |
160 | }
161 |
--------------------------------------------------------------------------------
/database/galleries.go:
--------------------------------------------------------------------------------
1 | package database
2 |
3 | import (
4 | "time"
5 |
6 | "github.com/backpulse/core/models"
7 | "gopkg.in/mgo.v2/bson"
8 | )
9 |
10 | // CreateGallery : insert new gallery in db
11 | func CreateGallery(gallery models.Gallery) error {
12 | err := DB.C(galleriesCollection).Insert(gallery)
13 | return err
14 | }
15 |
16 | // SetDefaultGallery : set a default gallery (useful for a homepage gallery for example)
17 | func SetDefaultGallery(site models.Site, gallery models.Gallery) error {
18 | _, err := DB.C(galleriesCollection).UpdateAll(bson.M{
19 | "site_id": site.ID,
20 | }, bson.M{
21 | "$set": bson.M{
22 | "default_gallery": false,
23 | },
24 | })
25 | if err != nil {
26 | return err
27 | }
28 | err = DB.C(galleriesCollection).UpdateId(gallery.ID, bson.M{
29 | "$set": bson.M{
30 | "default_gallery": true,
31 | },
32 | })
33 | return err
34 | }
35 |
36 | // UpdateGalleriesIndexes : Update galleries order
37 | func UpdateGalleriesIndexes(siteID bson.ObjectId, galleries []models.Gallery) error {
38 | for _, g := range galleries {
39 | err := DB.C(galleriesCollection).Update(bson.M{
40 | "site_id": siteID,
41 | "_id": g.ID,
42 | }, bson.M{
43 | "$set": bson.M{
44 | "index": g.Index,
45 | },
46 | })
47 | if err != nil {
48 | return err
49 | }
50 | }
51 | return nil
52 | }
53 |
54 | // GetGallery return specific gallery
55 | func GetGalleryByShortID(shortID string) (models.Gallery, error) {
56 | var gallery models.Gallery
57 | err := DB.C(galleriesCollection).Find(bson.M{
58 | "short_id": shortID,
59 | }).One(&gallery)
60 |
61 | if err != nil {
62 | return models.Gallery{}, err
63 | }
64 |
65 | photos, err := GetGalleryPhotos(gallery.ID)
66 | if err != nil {
67 | return models.Gallery{}, err
68 | }
69 |
70 | gallery.Photos = photos
71 | gallery.PreviewPhoto, _ = GetPhotoByID(gallery.PreviewPhotoID)
72 | gallery.Title = getDefaultGalleryTitle(gallery)
73 |
74 | return gallery, err
75 | }
76 |
77 | // GetGallery return specific gallery
78 | func GetGallery(id bson.ObjectId) (models.Gallery, error) {
79 | var gallery models.Gallery
80 | err := DB.C(galleriesCollection).FindId(id).One(&gallery)
81 |
82 | if err != nil {
83 | return models.Gallery{}, err
84 | }
85 |
86 | photos, err := GetGalleryPhotos(gallery.ID)
87 | if err != nil {
88 | return models.Gallery{}, err
89 | }
90 |
91 | gallery.Photos = photos
92 | gallery.PreviewPhoto, _ = GetPhotoByID(gallery.PreviewPhotoID)
93 | gallery.Title = getDefaultGalleryTitle(gallery)
94 |
95 | return gallery, err
96 | }
97 |
98 | // SetGalleryPreview : set a gallery preview image
99 | func SetGalleryPreview(gallery models.Gallery, photo models.Photo) error {
100 | err := DB.C(galleriesCollection).UpdateId(gallery.ID, bson.M{
101 | "$set": bson.M{
102 | "preview_photo_id": photo.ID,
103 | },
104 | })
105 | return err
106 | }
107 |
108 | // GetDefaultGallery return default gallery
109 | func GetDefaultGallery(id bson.ObjectId) (models.Gallery, error) {
110 | var gallery models.Gallery
111 | err := DB.C(galleriesCollection).Find(bson.M{
112 | "site_id": id,
113 | "default_gallery": true,
114 | }).One(&gallery)
115 |
116 | if err != nil {
117 | return models.Gallery{}, err
118 | }
119 |
120 | photos, err := GetGalleryPhotos(gallery.ID)
121 | if err != nil {
122 | return models.Gallery{}, err
123 | }
124 |
125 | gallery.PreviewPhoto, _ = GetPhotoByID(gallery.PreviewPhotoID)
126 |
127 | gallery.Photos = photos
128 |
129 | gallery.Title = getDefaultGalleryTitle(gallery)
130 |
131 | return gallery, err
132 | }
133 |
134 | // GetGalleries return site's galleries
135 | func GetGalleries(id bson.ObjectId) ([]models.Gallery, error) {
136 | var galleries []models.Gallery
137 | err := DB.C(galleriesCollection).Find(bson.M{
138 | "site_id": id,
139 | }).All(&galleries)
140 |
141 | if err != nil {
142 | return nil, err
143 | }
144 |
145 | for i := range galleries {
146 | title := getDefaultGalleryTitle(galleries[i])
147 | galleries[i].Title = title
148 | galleries[i].PreviewPhoto, _ = GetPhotoByID(galleries[i].PreviewPhotoID)
149 | }
150 |
151 | return galleries, err
152 | }
153 |
154 | // GetDefaultGalleryTitle : return default gallery title
155 | func getDefaultGalleryTitle(gallery models.Gallery) string {
156 | for i := range gallery.Titles {
157 | if gallery.Titles[i].LanguageCode == "en" {
158 | return gallery.Titles[i].Content
159 | }
160 | }
161 | return gallery.Titles[0].Content
162 | }
163 |
164 | // UpdateGallery update gallery
165 | func UpdateGallery(id bson.ObjectId, gallery models.Gallery) error {
166 | err := DB.C(galleriesCollection).UpdateId(id, bson.M{
167 | "$set": bson.M{
168 | "titles": gallery.Titles,
169 | "descriptions": gallery.Descriptions,
170 | "updated_at": time.Now(),
171 | },
172 | })
173 | return err
174 | }
175 |
176 | // DeleteGallery remove gallery from db
177 | func DeleteGallery(id bson.ObjectId) error {
178 | err := DB.C(galleriesCollection).RemoveId(id)
179 | if err != nil {
180 | return err
181 | }
182 | _, err = DB.C(photosCollection).RemoveAll(bson.M{
183 | "gallery_id": id,
184 | })
185 | return err
186 | }
187 |
--------------------------------------------------------------------------------
/database/users.go:
--------------------------------------------------------------------------------
1 | package database
2 |
3 | import (
4 | "net/http"
5 | "time"
6 |
7 | "github.com/asaskevich/govalidator"
8 | "github.com/backpulse/core/models"
9 | stripe "github.com/stripe/stripe-go"
10 | "gopkg.in/mgo.v2/bson"
11 | )
12 |
13 | // GetUserByEmail : return user by email
14 | func GetUserByEmail(email string) (models.User, error) {
15 | var user models.User
16 | err := DB.C(usersCollection).Find(bson.M{
17 | "email": email,
18 | }).One(&user)
19 | return user, err
20 | }
21 |
22 | // RemoveUserSubscription : remove pro subscription from user
23 | func RemoveUserSubscription(user models.User) error {
24 | err := DB.C(usersCollection).UpdateId(user.ID, bson.M{
25 | "$set": bson.M{
26 | "active_subscription_id": "",
27 | "professional": false,
28 | },
29 | })
30 | return err
31 | }
32 |
33 | // SetUserPro : add pro subscription to user
34 | func SetUserPro(id bson.ObjectId, customer *stripe.Customer, subscription *stripe.Subscription) error {
35 | err := DB.C(usersCollection).UpdateId(id, bson.M{
36 | "$set": bson.M{
37 | "professional": true,
38 | "stripe_id": customer.ID,
39 | "active_subscription_id": subscription.ID,
40 | },
41 | })
42 | return err
43 | }
44 |
45 | //UpdateUser : update user data
46 | func UpdateUser(user models.User, updateUser models.User) (models.User, error) {
47 | err := DB.C(usersCollection).UpdateId(user.ID, bson.M{
48 | "$set": bson.M{
49 | "fullname": updateUser.FullName,
50 | "updated_at": time.Now(),
51 | "address": updateUser.Address,
52 | "country": updateUser.Country,
53 | "zip": updateUser.ZIP,
54 | "city": updateUser.City,
55 | "state": updateUser.State,
56 | },
57 | })
58 | if err != nil {
59 | return models.User{}, err
60 | }
61 | var newUser models.User
62 | err = DB.C(usersCollection).FindId(user.ID).One(&newUser)
63 | if err != nil {
64 | return models.User{}, err
65 | }
66 | return newUser, nil
67 | }
68 |
69 | //InsertEmailVerification insert email verification object in db
70 | func InsertEmailVerification(verification models.EmailVerification) error {
71 | err := DB.C(emailVerificationsCollection).Insert(verification)
72 | return err
73 | }
74 |
75 | //GetVerificationByID : get email verification by id
76 | func GetVerificationByID(id bson.ObjectId) (models.EmailVerification, error) {
77 | var verification models.EmailVerification
78 | err := DB.C(emailVerificationsCollection).FindId(id).One(&verification)
79 | return verification, err
80 | }
81 |
82 | //DeleteVerification : remove verification from db
83 | func DeleteVerification(verification models.EmailVerification) error {
84 | err := DB.C(emailVerificationsCollection).RemoveId(verification.ID)
85 | return err
86 | }
87 |
88 | //VerifyUser : verify user
89 | func VerifyUser(user models.User, verification models.EmailVerification) error {
90 | err := DB.C(usersCollection).UpdateId(user.ID, bson.M{
91 | "$set": bson.M{
92 | "email": verification.Email,
93 | "email_verified": true,
94 | },
95 | })
96 | if err != nil {
97 | return err
98 | }
99 | err = DeleteVerification(verification)
100 | return err
101 | }
102 |
103 | // RemoveUser : remove user from db
104 | func RemoveUser(id bson.ObjectId) error {
105 | err := DB.C(usersCollection).RemoveId(id)
106 | return err
107 | }
108 |
109 | // UpdateUserPassword : update user password, password is of course hashed
110 | func UpdateUserPassword(id bson.ObjectId, password string) error {
111 | err := DB.C(usersCollection).UpdateId(id, bson.M{
112 | "$set": bson.M{
113 | "password": password,
114 | },
115 | })
116 | return err
117 | }
118 |
119 | //AddUser inserts user into Database
120 | func AddUser(user models.User) (models.User, error) {
121 | id := bson.NewObjectId()
122 | user.ID = id
123 | user.CreatedAt = time.Now()
124 | err := DB.C(usersCollection).Insert(&user)
125 | return user, err
126 | }
127 |
128 | //GetUser get user by username or email
129 | func GetUser(email string) (models.User, error) {
130 | var user models.User
131 | err := DB.C(usersCollection).Find(bson.M{
132 | "email": email,
133 | }).One(&user)
134 | return user, err
135 | }
136 |
137 | //GetUserByID : returns user object with id
138 | func GetUserByID(id bson.ObjectId) (models.User, error) {
139 | var user models.User
140 | err := DB.C(usersCollection).FindId(id).One(&user)
141 | return user, err
142 | }
143 |
144 | //IsEmailRegistered checks if email already exists in db
145 | func IsEmailRegistered(email string) bool {
146 | var user models.User
147 | _ = DB.C(usersCollection).Find(bson.M{
148 | "email": email,
149 | }).One(&user)
150 |
151 | return user.Email == email
152 | }
153 |
154 | //CreateVerification : insert a verification status in db
155 | func CreateVerification(user models.User) (models.EmailVerification, error) {
156 | id := bson.NewObjectId()
157 | verification := models.EmailVerification{
158 | UserID: user.ID,
159 | Email: user.Email,
160 | ExpireAt: time.Now().Add(time.Hour * time.Duration(24)),
161 | ID: id,
162 | }
163 | err := InsertEmailVerification(verification)
164 | return verification, err
165 | }
166 |
167 | //VerifyEmail : check email format and whether it's been assigned already or not
168 | func VerifyEmail(email string, r *http.Request) (bool, string) {
169 | /* Check email */
170 | if !govalidator.IsEmail(email) {
171 | return false, "invalid_email"
172 | }
173 |
174 | /* Check if email was used already */
175 | if IsEmailRegistered(email) {
176 | return false, "email_exists"
177 | }
178 | return true, ""
179 | }
180 |
--------------------------------------------------------------------------------
/handlers/admin/tracks.go:
--------------------------------------------------------------------------------
1 | package adminhandlers
2 |
3 | import (
4 | "encoding/json"
5 | "log"
6 | "net/http"
7 |
8 | "github.com/backpulse/core/database"
9 | "github.com/backpulse/core/models"
10 | "github.com/backpulse/core/utils"
11 | "github.com/gorilla/mux"
12 | "github.com/teris-io/shortid"
13 | "gopkg.in/mgo.v2/bson"
14 | )
15 |
16 | // AddTrack : add track to album
17 | func AddTrack(w http.ResponseWriter, r *http.Request) {
18 | vars := mux.Vars(r)
19 | name := vars["name"]
20 | albumid := vars["albumid"]
21 |
22 | site, _ := database.GetSiteByName(name)
23 | user, _ := database.GetUserByID(utils.GetUserObjectID(r))
24 |
25 | if !utils.IsAuthorized(site, user) {
26 | utils.RespondWithJSON(w, http.StatusUnauthorized, "unauthorized", nil)
27 | return
28 | }
29 |
30 | album, err := database.GetAlbum(bson.ObjectIdHex(albumid))
31 | if err != nil {
32 | utils.RespondWithJSON(w, http.StatusNotFound, "not_found", nil)
33 | return
34 | }
35 |
36 | if album.SiteID != site.ID {
37 | utils.RespondWithJSON(w, http.StatusUnauthorized, "unauthorized", nil)
38 | return
39 | }
40 |
41 | var track models.Track
42 | /* Parse json to models.Track */
43 | err = json.NewDecoder(r.Body).Decode(&track)
44 | if err != nil {
45 | utils.RespondWithJSON(w, http.StatusNotAcceptable, "error", nil)
46 | return
47 | }
48 |
49 | track.AlbumID = bson.ObjectIdHex(albumid)
50 |
51 | if track.AlbumID == "" {
52 | utils.RespondWithJSON(w, http.StatusNotAcceptable, "album_id_required", nil)
53 | return
54 | }
55 |
56 | track.SiteID = site.ID
57 | track.OwnerID = site.OwnerID
58 | track.ShortID, _ = shortid.Generate()
59 | track.ID = bson.NewObjectId()
60 |
61 | tracks, _ := database.GetAlbumTracks(track.AlbumID)
62 | track.Index = len(tracks)
63 |
64 | err = database.AddTrack(track)
65 | if err != nil {
66 | utils.RespondWithJSON(w, http.StatusInternalServerError, "error", nil)
67 | return
68 | }
69 |
70 | utils.RespondWithJSON(w, http.StatusOK, "success", nil)
71 | return
72 | }
73 |
74 | // UpdateTrack : update track informations
75 | func UpdateTrack(w http.ResponseWriter, r *http.Request) {
76 | vars := mux.Vars(r)
77 | name := vars["name"]
78 | id := vars["id"]
79 |
80 | site, _ := database.GetSiteByName(name)
81 | user, _ := database.GetUserByID(utils.GetUserObjectID(r))
82 |
83 | if !utils.IsAuthorized(site, user) {
84 | utils.RespondWithJSON(w, http.StatusUnauthorized, "unauthorized", nil)
85 | return
86 | }
87 |
88 | var track models.Track
89 | /* Parse json to models.Track */
90 | err := json.NewDecoder(r.Body).Decode(&track)
91 | if err != nil {
92 | utils.RespondWithJSON(w, http.StatusNotAcceptable, "error", nil)
93 | return
94 | }
95 |
96 | err = database.UpdateTrack(bson.ObjectIdHex(id), track)
97 | if err != nil {
98 | utils.RespondWithJSON(w, http.StatusInternalServerError, "error", nil)
99 | return
100 | }
101 |
102 | utils.RespondWithJSON(w, http.StatusOK, "success", nil)
103 | return
104 | }
105 |
106 | // DeleteTrack : remove track from album
107 | func DeleteTrack(w http.ResponseWriter, r *http.Request) {
108 | vars := mux.Vars(r)
109 | name := vars["name"]
110 | id := vars["id"]
111 |
112 | site, _ := database.GetSiteByName(name)
113 | user, _ := database.GetUserByID(utils.GetUserObjectID(r))
114 |
115 | if !utils.IsAuthorized(site, user) {
116 | utils.RespondWithJSON(w, http.StatusUnauthorized, "unauthorized", nil)
117 | return
118 | }
119 |
120 | track, err := database.GetTrack(bson.ObjectIdHex(id))
121 | if err != nil {
122 | utils.RespondWithJSON(w, http.StatusNotFound, "not_found", nil)
123 | return
124 | }
125 |
126 | err = database.RemoveTrack(track.ID)
127 | if err != nil {
128 | utils.RespondWithJSON(w, http.StatusInternalServerError, "error", nil)
129 | return
130 | }
131 |
132 | utils.RespondWithJSON(w, http.StatusOK, "success", nil)
133 | return
134 | }
135 |
136 | // GetTrack : get specific track
137 | func GetTrack(w http.ResponseWriter, r *http.Request) {
138 | vars := mux.Vars(r)
139 | name := vars["name"]
140 | id := vars["id"]
141 |
142 | site, _ := database.GetSiteByName(name)
143 | user, _ := database.GetUserByID(utils.GetUserObjectID(r))
144 |
145 | if !utils.IsAuthorized(site, user) {
146 | utils.RespondWithJSON(w, http.StatusUnauthorized, "unauthorized", nil)
147 | return
148 | }
149 |
150 | track, err := database.GetTrack(bson.ObjectIdHex(id))
151 | if err != nil {
152 | utils.RespondWithJSON(w, http.StatusNotFound, "not_found", nil)
153 | return
154 | }
155 |
156 | utils.RespondWithJSON(w, http.StatusOK, "success", track)
157 | return
158 | }
159 |
160 | // UpdateTracksIndexes : update tracks order
161 | func UpdateTracksIndexes(w http.ResponseWriter, r *http.Request) {
162 | vars := mux.Vars(r)
163 | siteName := vars["name"]
164 |
165 | site, _ := database.GetSiteByName(siteName)
166 | user, _ := database.GetUserByID(utils.GetUserObjectID(r))
167 |
168 | if !utils.IsAuthorized(site, user) {
169 | utils.RespondWithJSON(w, http.StatusUnauthorized, "unauthorized", nil)
170 | return
171 | }
172 |
173 | var tracks []models.Track
174 | /* Parse json to models.Gallery */
175 | err := json.NewDecoder(r.Body).Decode(&tracks)
176 | if err != nil {
177 | log.Print(err)
178 | utils.RespondWithJSON(w, http.StatusNotAcceptable, "error", nil)
179 | return
180 | }
181 |
182 | err = database.UpdateTracksIndexes(site.ID, tracks)
183 | if err != nil {
184 | log.Print(err)
185 | utils.RespondWithJSON(w, http.StatusNotAcceptable, "error", nil)
186 | return
187 | }
188 | utils.RespondWithJSON(w, http.StatusOK, "success", nil)
189 | return
190 | }
191 |
--------------------------------------------------------------------------------
/handlers/admin/videogroups.go:
--------------------------------------------------------------------------------
1 | package adminhandlers
2 |
3 | import (
4 | "encoding/json"
5 | "log"
6 | "net/http"
7 |
8 | "github.com/backpulse/core/database"
9 | "github.com/backpulse/core/models"
10 | "github.com/backpulse/core/utils"
11 | "github.com/gorilla/mux"
12 | "github.com/teris-io/shortid"
13 | "gopkg.in/mgo.v2/bson"
14 | )
15 |
16 | func CreateVideoGroup(w http.ResponseWriter, r *http.Request) {
17 | vars := mux.Vars(r)
18 | name := vars["name"]
19 |
20 | site, _ := database.GetSiteByName(name)
21 | user, _ := database.GetUserByID(utils.GetUserObjectID(r))
22 |
23 | if !utils.IsAuthorized(site, user) {
24 | utils.RespondWithJSON(w, http.StatusUnauthorized, "unauthorized", nil)
25 | return
26 | }
27 |
28 | var videoGroup models.VideoGroup
29 | /* Parse json to models.Project */
30 | err := json.NewDecoder(r.Body).Decode(&videoGroup)
31 | if err != nil {
32 | utils.RespondWithJSON(w, http.StatusNotAcceptable, "error", nil)
33 | return
34 | }
35 |
36 | videoGroup.ShortID, _ = shortid.Generate()
37 | videoGroup.SiteID = site.ID
38 | videoGroup.OwnerID = site.OwnerID
39 | videoGroup.ID = bson.NewObjectId()
40 |
41 | groups, _ := database.GetVideoGroups(site.ID)
42 | videoGroup.Index = len(groups)
43 |
44 | err = database.AddVideoGroup(videoGroup)
45 | if err != nil {
46 | log.Print(err)
47 | utils.RespondWithJSON(w, http.StatusInternalServerError, "error", nil)
48 | return
49 | }
50 |
51 | utils.RespondWithJSON(w, http.StatusOK, "success", nil)
52 | return
53 | }
54 |
55 | func GetVideoGroups(w http.ResponseWriter, r *http.Request) {
56 | vars := mux.Vars(r)
57 | name := vars["name"]
58 |
59 | site, _ := database.GetSiteByName(name)
60 | user, _ := database.GetUserByID(utils.GetUserObjectID(r))
61 |
62 | if !utils.IsAuthorized(site, user) {
63 | utils.RespondWithJSON(w, http.StatusUnauthorized, "unauthorized", nil)
64 | return
65 | }
66 |
67 | videoGroups, err := database.GetVideoGroups(site.ID)
68 | if err != nil {
69 | utils.RespondWithJSON(w, http.StatusInternalServerError, "error", nil)
70 | return
71 | }
72 |
73 | utils.RespondWithJSON(w, http.StatusOK, "success", videoGroups)
74 | return
75 | }
76 |
77 | func GetVideoGroup(w http.ResponseWriter, r *http.Request) {
78 | vars := mux.Vars(r)
79 | name := vars["name"]
80 | id := vars["id"]
81 |
82 | site, _ := database.GetSiteByName(name)
83 | user, _ := database.GetUserByID(utils.GetUserObjectID(r))
84 |
85 | if !utils.IsAuthorized(site, user) {
86 | utils.RespondWithJSON(w, http.StatusUnauthorized, "unauthorized", nil)
87 | return
88 | }
89 |
90 | videoGroup, err := database.GetVideoGroup(bson.ObjectIdHex(id))
91 | if err != nil {
92 | utils.RespondWithJSON(w, http.StatusInternalServerError, "error", nil)
93 | return
94 | }
95 |
96 | if videoGroup.SiteID != site.ID {
97 | utils.RespondWithJSON(w, http.StatusUnauthorized, "unauthorized", nil)
98 | return
99 | }
100 |
101 | videos, _ := database.GetGroupVideos(videoGroup.ID)
102 | videoGroup.Videos = videos
103 |
104 | utils.RespondWithJSON(w, http.StatusOK, "success", videoGroup)
105 | return
106 | }
107 | func DeleteVideoGroup(w http.ResponseWriter, r *http.Request) {
108 | vars := mux.Vars(r)
109 | name := vars["name"]
110 | id := vars["id"]
111 |
112 | site, _ := database.GetSiteByName(name)
113 | user, _ := database.GetUserByID(utils.GetUserObjectID(r))
114 |
115 | if !utils.IsAuthorized(site, user) {
116 | utils.RespondWithJSON(w, http.StatusUnauthorized, "unauthorized", nil)
117 | return
118 | }
119 |
120 | videoGroup, err := database.GetVideoGroup(bson.ObjectIdHex(id))
121 | if videoGroup.SiteID != site.ID {
122 | utils.RespondWithJSON(w, http.StatusUnauthorized, "unauthorized", nil)
123 | return
124 | }
125 |
126 | for _, video := range videoGroup.Videos {
127 | database.RemoveVideo(video.ID)
128 | }
129 |
130 | err = database.RemoveVideoGroup(videoGroup.ID)
131 | if err != nil {
132 | utils.RespondWithJSON(w, http.StatusInternalServerError, "error", nil)
133 | return
134 | }
135 |
136 | utils.RespondWithJSON(w, http.StatusOK, "success", nil)
137 | return
138 | }
139 |
140 | // UpdateVideoGroup : rename & change image
141 | func UpdateVideoGroup(w http.ResponseWriter, r *http.Request) {
142 | vars := mux.Vars(r)
143 | siteName := vars["name"]
144 | id := vars["id"]
145 |
146 | site, _ := database.GetSiteByName(siteName)
147 | user, _ := database.GetUserByID(utils.GetUserObjectID(r))
148 |
149 | if !utils.IsAuthorized(site, user) {
150 | utils.RespondWithJSON(w, http.StatusUnauthorized, "unauthorized", nil)
151 | return
152 | }
153 | var group models.VideoGroup
154 | /* Parse json to models.Gallery */
155 | err := json.NewDecoder(r.Body).Decode(&group)
156 | if err != nil {
157 | utils.RespondWithJSON(w, http.StatusNotAcceptable, "error", nil)
158 | return
159 | }
160 |
161 | g, err := database.GetVideoGroup(bson.ObjectIdHex(id))
162 | if err != nil {
163 | utils.RespondWithJSON(w, http.StatusNotFound, "not_found", nil)
164 | return
165 | }
166 | if g.SiteID != site.ID {
167 | utils.RespondWithJSON(w, http.StatusUnauthorized, "unauthorized", nil)
168 | return
169 | }
170 |
171 | err = database.UpdateVideoGroup(bson.ObjectIdHex(id), group)
172 | if err != nil {
173 | utils.RespondWithJSON(w, http.StatusInternalServerError, "error", nil)
174 | return
175 | }
176 | utils.RespondWithJSON(w, http.StatusOK, "success", nil)
177 | return
178 | }
179 |
180 | func UpdateVideoGroupsIndexes(w http.ResponseWriter, r *http.Request) {
181 | vars := mux.Vars(r)
182 | siteName := vars["name"]
183 |
184 | site, _ := database.GetSiteByName(siteName)
185 | user, _ := database.GetUserByID(utils.GetUserObjectID(r))
186 |
187 | if !utils.IsAuthorized(site, user) {
188 | utils.RespondWithJSON(w, http.StatusUnauthorized, "unauthorized", nil)
189 | return
190 | }
191 |
192 | var groups []models.VideoGroup
193 | /* Parse json to models.Gallery */
194 | err := json.NewDecoder(r.Body).Decode(&groups)
195 | if err != nil {
196 | utils.RespondWithJSON(w, http.StatusNotAcceptable, "error", nil)
197 | return
198 | }
199 |
200 | err = database.UpdateVideoGroupsIndexes(site.ID, groups)
201 | if err != nil {
202 | utils.RespondWithJSON(w, http.StatusNotAcceptable, "error", nil)
203 | return
204 | }
205 | utils.RespondWithJSON(w, http.StatusOK, "success", nil)
206 | return
207 | }
208 |
--------------------------------------------------------------------------------
/handlers/admin/albums.go:
--------------------------------------------------------------------------------
1 | package adminhandlers
2 |
3 | import (
4 | "encoding/json"
5 | "log"
6 | "net/http"
7 |
8 | "github.com/backpulse/core/database"
9 | "github.com/backpulse/core/models"
10 | "github.com/backpulse/core/utils"
11 | "github.com/gorilla/mux"
12 | "github.com/teris-io/shortid"
13 | "gopkg.in/mgo.v2/bson"
14 | )
15 |
16 | // CreateAlbum : create new album
17 | func CreateAlbum(w http.ResponseWriter, r *http.Request) {
18 | vars := mux.Vars(r)
19 | name := vars["name"]
20 |
21 | site, _ := database.GetSiteByName(name)
22 | user, _ := database.GetUserByID(utils.GetUserObjectID(r))
23 |
24 | if !utils.IsAuthorized(site, user) {
25 | utils.RespondWithJSON(w, http.StatusUnauthorized, "unauthorized", nil)
26 | return
27 | }
28 |
29 | var album models.Album
30 | /* Parse json to models.Album */
31 | err := json.NewDecoder(r.Body).Decode(&album)
32 | if err != nil {
33 | utils.RespondWithJSON(w, http.StatusNotAcceptable, "error", nil)
34 | return
35 | }
36 |
37 | album.ShortID, _ = shortid.Generate()
38 | album.SiteID = site.ID
39 | album.OwnerID = site.OwnerID
40 | album.ID = bson.NewObjectId()
41 |
42 | albums, _ := database.GetAlbums(site.ID)
43 | album.Index = len(albums)
44 |
45 | err = database.AddAlbum(album)
46 | if err != nil {
47 | log.Print(err)
48 | utils.RespondWithJSON(w, http.StatusInternalServerError, "error", nil)
49 | return
50 | }
51 |
52 | utils.RespondWithJSON(w, http.StatusOK, "success", nil)
53 | return
54 | }
55 |
56 | // GetAlbums : return albums of site
57 | func GetAlbums(w http.ResponseWriter, r *http.Request) {
58 | vars := mux.Vars(r)
59 | name := vars["name"]
60 |
61 | site, _ := database.GetSiteByName(name)
62 | user, _ := database.GetUserByID(utils.GetUserObjectID(r))
63 |
64 | if !utils.IsAuthorized(site, user) {
65 | utils.RespondWithJSON(w, http.StatusUnauthorized, "unauthorized", nil)
66 | return
67 | }
68 |
69 | albums, err := database.GetAlbums(site.ID)
70 | if err != nil {
71 | utils.RespondWithJSON(w, http.StatusInternalServerError, "error", nil)
72 | return
73 | }
74 |
75 | utils.RespondWithJSON(w, http.StatusOK, "success", albums)
76 | return
77 | }
78 |
79 | // GetAlbum : return specific album
80 | func GetAlbum(w http.ResponseWriter, r *http.Request) {
81 | vars := mux.Vars(r)
82 | name := vars["name"]
83 | id := vars["id"]
84 |
85 | site, _ := database.GetSiteByName(name)
86 | user, _ := database.GetUserByID(utils.GetUserObjectID(r))
87 |
88 | if !utils.IsAuthorized(site, user) {
89 | utils.RespondWithJSON(w, http.StatusUnauthorized, "unauthorized", nil)
90 | return
91 | }
92 |
93 | album, err := database.GetAlbum(bson.ObjectIdHex(id))
94 | if err != nil {
95 | utils.RespondWithJSON(w, http.StatusInternalServerError, "error", nil)
96 | return
97 | }
98 |
99 | if album.SiteID != site.ID {
100 | utils.RespondWithJSON(w, http.StatusUnauthorized, "unauthorized", nil)
101 | return
102 | }
103 |
104 | tracks, _ := database.GetAlbumTracks(album.ID)
105 | album.Tracks = tracks
106 |
107 | utils.RespondWithJSON(w, http.StatusOK, "success", album)
108 | return
109 | }
110 |
111 | // DeleteAlbum : remove album from db
112 | func DeleteAlbum(w http.ResponseWriter, r *http.Request) {
113 | vars := mux.Vars(r)
114 | name := vars["name"]
115 | id := vars["id"]
116 |
117 | site, _ := database.GetSiteByName(name)
118 | user, _ := database.GetUserByID(utils.GetUserObjectID(r))
119 |
120 | if !utils.IsAuthorized(site, user) {
121 | utils.RespondWithJSON(w, http.StatusUnauthorized, "unauthorized", nil)
122 | return
123 | }
124 |
125 | album, err := database.GetAlbum(bson.ObjectIdHex(id))
126 | if album.SiteID != site.ID {
127 | utils.RespondWithJSON(w, http.StatusUnauthorized, "unauthorized", nil)
128 | return
129 | }
130 |
131 | for _, track := range album.Tracks {
132 | database.RemoveTrack(track.ID)
133 | }
134 |
135 | err = database.RemoveAlbum(album.ID)
136 | if err != nil {
137 | utils.RespondWithJSON(w, http.StatusInternalServerError, "error", nil)
138 | return
139 | }
140 |
141 | utils.RespondWithJSON(w, http.StatusOK, "success", nil)
142 | return
143 | }
144 |
145 | // UpdateAlbum : rename & change image
146 | func UpdateAlbum(w http.ResponseWriter, r *http.Request) {
147 | vars := mux.Vars(r)
148 | siteName := vars["name"]
149 | id := vars["id"]
150 |
151 | site, _ := database.GetSiteByName(siteName)
152 | user, _ := database.GetUserByID(utils.GetUserObjectID(r))
153 |
154 | if !utils.IsAuthorized(site, user) {
155 | utils.RespondWithJSON(w, http.StatusUnauthorized, "unauthorized", nil)
156 | return
157 | }
158 | var album models.Album
159 | /* Parse json to models.Album */
160 | err := json.NewDecoder(r.Body).Decode(&album)
161 | if err != nil {
162 | utils.RespondWithJSON(w, http.StatusNotAcceptable, "error", nil)
163 | return
164 | }
165 |
166 | a, err := database.GetAlbum(bson.ObjectIdHex(id))
167 | if err != nil {
168 | utils.RespondWithJSON(w, http.StatusNotFound, "not_found", nil)
169 | return
170 | }
171 | if a.SiteID != site.ID {
172 | utils.RespondWithJSON(w, http.StatusUnauthorized, "unauthorized", nil)
173 | return
174 | }
175 |
176 | err = database.UpdateAlbum(bson.ObjectIdHex(id), album)
177 | if err != nil {
178 | utils.RespondWithJSON(w, http.StatusInternalServerError, "error", nil)
179 | return
180 | }
181 | utils.RespondWithJSON(w, http.StatusOK, "success", nil)
182 | return
183 | }
184 |
185 | // UpdateAlbumsIndxes : update order of albums
186 | func UpdateAlbumsIndexes(w http.ResponseWriter, r *http.Request) {
187 | vars := mux.Vars(r)
188 | siteName := vars["name"]
189 |
190 | site, _ := database.GetSiteByName(siteName)
191 | user, _ := database.GetUserByID(utils.GetUserObjectID(r))
192 |
193 | if !utils.IsAuthorized(site, user) {
194 | utils.RespondWithJSON(w, http.StatusUnauthorized, "unauthorized", nil)
195 | return
196 | }
197 |
198 | var albums []models.Album
199 | /* Parse json to models.Album */
200 | err := json.NewDecoder(r.Body).Decode(&albums)
201 | if err != nil {
202 | utils.RespondWithJSON(w, http.StatusNotAcceptable, "error", nil)
203 | return
204 | }
205 |
206 | err = database.UpdateAlbumsIndexes(site.ID, albums)
207 | if err != nil {
208 | utils.RespondWithJSON(w, http.StatusNotAcceptable, "error", nil)
209 | return
210 | }
211 | utils.RespondWithJSON(w, http.StatusOK, "success", nil)
212 | return
213 | }
214 |
--------------------------------------------------------------------------------
/handlers/admin/videos.go:
--------------------------------------------------------------------------------
1 | package adminhandlers
2 |
3 | import (
4 | "encoding/json"
5 | "log"
6 | "net/http"
7 | "regexp"
8 |
9 | "github.com/backpulse/core/database"
10 | "github.com/backpulse/core/models"
11 | "github.com/backpulse/core/utils"
12 | "github.com/gorilla/mux"
13 | "github.com/teris-io/shortid"
14 | "gopkg.in/mgo.v2/bson"
15 | )
16 |
17 | // AddVideo : add video to video group
18 | func AddVideo(w http.ResponseWriter, r *http.Request) {
19 | vars := mux.Vars(r)
20 | name := vars["name"]
21 | groupid := vars["groupid"]
22 |
23 | site, _ := database.GetSiteByName(name)
24 | user, _ := database.GetUserByID(utils.GetUserObjectID(r))
25 |
26 | if !utils.IsAuthorized(site, user) {
27 | utils.RespondWithJSON(w, http.StatusUnauthorized, "unauthorized", nil)
28 | return
29 | }
30 |
31 | group, err := database.GetVideoGroup(bson.ObjectIdHex(groupid))
32 | if err != nil {
33 | utils.RespondWithJSON(w, http.StatusNotFound, "not_found", nil)
34 | return
35 | }
36 |
37 | if group.SiteID != site.ID {
38 | utils.RespondWithJSON(w, http.StatusUnauthorized, "unauthorized", nil)
39 | return
40 | }
41 |
42 | var video models.Video
43 | /* Parse json to models.Project */
44 | err = json.NewDecoder(r.Body).Decode(&video)
45 | if err != nil {
46 | utils.RespondWithJSON(w, http.StatusNotAcceptable, "error", nil)
47 | return
48 | }
49 |
50 | video.VideoGroupID = bson.ObjectIdHex(groupid)
51 |
52 | if len(video.YouTubeURL) < 1 {
53 | utils.RespondWithJSON(w, http.StatusNotAcceptable, "url_required", nil)
54 | return
55 | }
56 | match, _ := regexp.MatchString("^(http(s)?:\\/\\/)?((w){3}.)?youtu(be|.be)?(\\.com)?\\/.+", video.YouTubeURL)
57 | if !match {
58 | utils.RespondWithJSON(w, http.StatusNotAcceptable, "url_required", nil)
59 | return
60 | }
61 |
62 | if video.VideoGroupID == "" {
63 | utils.RespondWithJSON(w, http.StatusNotAcceptable, "video_group_required", nil)
64 | return
65 | }
66 |
67 | video.SiteID = site.ID
68 | video.OwnerID = site.OwnerID
69 | video.ShortID, _ = shortid.Generate()
70 | video.ID = bson.NewObjectId()
71 |
72 | videos, _ := database.GetGroupVideos(video.VideoGroupID)
73 | video.Index = len(videos)
74 |
75 | err = database.AddVideo(video)
76 | if err != nil {
77 | utils.RespondWithJSON(w, http.StatusInternalServerError, "error", nil)
78 | return
79 | }
80 |
81 | utils.RespondWithJSON(w, http.StatusOK, "success", nil)
82 | return
83 | }
84 |
85 | // UpdateVideo : update informations of video (title, url)
86 | func UpdateVideo(w http.ResponseWriter, r *http.Request) {
87 | vars := mux.Vars(r)
88 | name := vars["name"]
89 | id := vars["id"]
90 |
91 | site, _ := database.GetSiteByName(name)
92 | user, _ := database.GetUserByID(utils.GetUserObjectID(r))
93 |
94 | if !utils.IsAuthorized(site, user) {
95 | utils.RespondWithJSON(w, http.StatusUnauthorized, "unauthorized", nil)
96 | return
97 | }
98 |
99 | var video models.Video
100 | /* Parse json to models.Project */
101 | err := json.NewDecoder(r.Body).Decode(&video)
102 | if err != nil {
103 | utils.RespondWithJSON(w, http.StatusNotAcceptable, "error", nil)
104 | return
105 | }
106 |
107 | if len(video.YouTubeURL) < 1 {
108 | utils.RespondWithJSON(w, http.StatusNotAcceptable, "url_required", nil)
109 | return
110 | }
111 | match, _ := regexp.MatchString("^(http(s)?:\\/\\/)?((w){3}.)?youtu(be|.be)?(\\.com)?\\/.+", video.YouTubeURL)
112 | if !match {
113 | utils.RespondWithJSON(w, http.StatusNotAcceptable, "url_required", nil)
114 | return
115 | }
116 |
117 | err = database.UpdateVideo(bson.ObjectIdHex(id), video)
118 | if err != nil {
119 | utils.RespondWithJSON(w, http.StatusInternalServerError, "error", nil)
120 | return
121 | }
122 |
123 | utils.RespondWithJSON(w, http.StatusOK, "success", nil)
124 | return
125 | }
126 |
127 | // DeleteVideo : remove video from video group
128 | func DeleteVideo(w http.ResponseWriter, r *http.Request) {
129 | vars := mux.Vars(r)
130 | name := vars["name"]
131 | id := vars["id"]
132 |
133 | site, _ := database.GetSiteByName(name)
134 | user, _ := database.GetUserByID(utils.GetUserObjectID(r))
135 |
136 | if !utils.IsAuthorized(site, user) {
137 | utils.RespondWithJSON(w, http.StatusUnauthorized, "unauthorized", nil)
138 | return
139 | }
140 |
141 | video, err := database.GetVideo(bson.ObjectIdHex(id))
142 | if err != nil {
143 | utils.RespondWithJSON(w, http.StatusNotFound, "not_found", nil)
144 | return
145 | }
146 |
147 | err = database.RemoveVideo(video.ID)
148 | if err != nil {
149 | utils.RespondWithJSON(w, http.StatusInternalServerError, "error", nil)
150 | return
151 | }
152 |
153 | utils.RespondWithJSON(w, http.StatusOK, "success", nil)
154 | return
155 | }
156 |
157 | // GetVideo : get specific video
158 | func GetVideo(w http.ResponseWriter, r *http.Request) {
159 | vars := mux.Vars(r)
160 | name := vars["name"]
161 | id := vars["id"]
162 |
163 | site, _ := database.GetSiteByName(name)
164 | user, _ := database.GetUserByID(utils.GetUserObjectID(r))
165 |
166 | if !utils.IsAuthorized(site, user) {
167 | utils.RespondWithJSON(w, http.StatusUnauthorized, "unauthorized", nil)
168 | return
169 | }
170 |
171 | video, err := database.GetVideo(bson.ObjectIdHex(id))
172 | if err != nil {
173 | utils.RespondWithJSON(w, http.StatusNotFound, "not_found", nil)
174 | return
175 | }
176 |
177 | utils.RespondWithJSON(w, http.StatusOK, "success", video)
178 | return
179 | }
180 |
181 | // UpdateVideoIndexes : update order of videos in video group
182 | func UpdateVideosIndexes(w http.ResponseWriter, r *http.Request) {
183 | vars := mux.Vars(r)
184 | siteName := vars["name"]
185 |
186 | site, _ := database.GetSiteByName(siteName)
187 | user, _ := database.GetUserByID(utils.GetUserObjectID(r))
188 |
189 | if !utils.IsAuthorized(site, user) {
190 | utils.RespondWithJSON(w, http.StatusUnauthorized, "unauthorized", nil)
191 | return
192 | }
193 |
194 | var videos []models.Video
195 | /* Parse json to models.Gallery */
196 | err := json.NewDecoder(r.Body).Decode(&videos)
197 | if err != nil {
198 | log.Print(err)
199 | utils.RespondWithJSON(w, http.StatusNotAcceptable, "error", nil)
200 | return
201 | }
202 |
203 | err = database.UpdateVideosIndexes(site.ID, videos)
204 | if err != nil {
205 | log.Print(err)
206 |
207 | utils.RespondWithJSON(w, http.StatusNotAcceptable, "error", nil)
208 | return
209 | }
210 | utils.RespondWithJSON(w, http.StatusOK, "success", nil)
211 | return
212 | }
213 |
--------------------------------------------------------------------------------
/routes/client/documentation.md:
--------------------------------------------------------------------------------
1 | ## Backpulse API
2 |
3 | This is the official Backpulse API.
4 | API Endpoint:
5 | ```
6 | https://api.backpulse.io/:sitename
7 | ```
8 | #### Successful request
9 | Example of a successful request.
10 | ```json
11 | {
12 | "status": "success",
13 | "code": 200,
14 | "message": "success",
15 | "payload": {...}
16 | }
17 | ```
18 |
19 | #### Unsuccessful request
20 | Exemple of 404 error.
21 | ```json
22 | {
23 | "status": "error",
24 | "code": 404,
25 | "message": "not_found",
26 | "payload": null
27 | }
28 | ```
29 |
30 | ### Models
31 | Examples of the API models.
32 |
33 | #### Gallery
34 | ```json
35 | {
36 | "short_id": "ef855uQmR",
37 | "site_name": "mysite",
38 | "title": string, // English title
39 | "titles": []Translation,
40 | "descriptions": []Translation,
41 | "photos": []Photo,
42 | "created_at": Date,
43 | "updated_at": Date
44 | }
45 | ```
46 |
47 | #### Project
48 | ```json
49 | {
50 | "short_id": "NzFVjUwig",
51 | "site_name": "aureleoules",
52 | "title": string, // English title
53 | "titles": []Translation,
54 | "descriptions": []Translation,
55 | "url": string,
56 | "created_at": Date,
57 | "updated_at": Date
58 | }
59 | ```
60 |
61 | #### Translation
62 | ```json
63 | {
64 | "language_name": "English",
65 | "language_code": "en",
66 | "content": string
67 | }
68 | ```
69 | #### Photo
70 | ```json
71 | {
72 | "url": "https://res.cloudinary.com/zygtpradi/image/authenticated/x--3BfHaB4O--/v1547280893/le6bd7z918vc8qas1s.jpg",
73 | "width": 1920,
74 | "height": 1080,
75 | "format": "jpg",
76 | "index": 0,
77 | "created_at": Date
78 | }
79 | ```
80 |
81 | ####
82 |
83 |
84 | ## Routes
85 | List of all the API routes.
86 |
87 | ### Galleries
88 |
89 | Lists all galleries.
90 |
91 | ```endpoint
92 | GET /galleries
93 | ```
94 |
95 | #### Example request
96 |
97 | ```curl
98 | $ curl https://api.backpulse.io/mysite/galleries
99 | ```
100 |
101 | #### Example response
102 |
103 | ```json
104 | {
105 | "status": "success",
106 | "code": 200,
107 | "message": "success",
108 | "payload": [
109 | {
110 | "short_id": "eS9u5RQmR",
111 | "site_name": "mysite",
112 | "title": string,
113 | "titles": []Translation,
114 | "descriptions": []Translation,
115 | "photos": []Photo,
116 | "created_at": Date,
117 | "updated_at": Date
118 | },
119 | ...
120 | ]
121 | }
122 | ```
123 |
124 | ### Gallery
125 |
126 | Get a specific gallery.
127 |
128 | ```endpoint
129 | GET /gallery/:short_id
130 | ```
131 | Route parameter | Description
132 | --- | ---
133 | `short_id` | id of an existing gallery
134 |
135 | #### Example request
136 |
137 | ```curl
138 | $ curl https://api.backpulse.io/mysite/gallery/NzFVjUwig
139 | ```
140 |
141 | #### Example response
142 |
143 | ```json
144 | {
145 | "status": "success",
146 | "code": 200,
147 | "message": "success",
148 | "payload": {
149 | "short_id": "eS9u5RQmR",
150 | "site_name": "mysite",
151 | "title": string,
152 | "titles": []Translation,
153 | "descriptions": []Translation,
154 | "photos": []Photo,
155 | "created_at": Date,
156 | "updated_at": Date
157 | }
158 | }
159 | ```
160 |
161 | ### Default gallery
162 |
163 | Get the default gallery.
164 |
165 | ```endpoint
166 | GET /galleries/home
167 | ```
168 |
169 | #### Example request
170 |
171 | ```curl
172 | $ curl https://api.backpulse.io/mysite/galleries/home
173 | ```
174 |
175 | #### Example response
176 |
177 | ```json
178 | {
179 | "status": "success",
180 | "code": 200,
181 | "message": "success",
182 | "payload": {
183 | "short_id": "eS9u5RQmR",
184 | "site_name": "mysite",
185 | "title": string,
186 | "titles": []Translation,
187 | "descriptions": []Translation,
188 | "photos": []Photo,
189 | "created_at": Date,
190 | "updated_at": Date
191 | }
192 | }
193 | ```
194 |
195 | ### Projects
196 |
197 | Lists all projects.
198 |
199 | ```endpoint
200 | GET /projects
201 | ```
202 |
203 | #### Example request
204 |
205 | ```curl
206 | $ curl https://api.backpulse.io/mysite/projects
207 | ```
208 |
209 | #### Example response
210 |
211 | ```json
212 | {
213 | "status": "success",
214 | "code": 200,
215 | "message": "success",
216 | "payload": [
217 | {
218 | "short_id": "eS9u5RQmR",
219 | "site_name": "mysite",
220 | "title": string,
221 | "titles": []Translation,
222 | "descriptions": []Translation,
223 | "url": string,
224 | "created_at": Date,
225 | "updated_at": Date
226 | },
227 | ...
228 | ]
229 | }
230 | ```
231 |
232 | ### Project
233 |
234 | Get a specific project.
235 |
236 | ```endpoint
237 | GET /project/:short_id
238 | ```
239 | Route parameter | Description
240 | --- | ---
241 | `short_id` | id of an existing project
242 |
243 | #### Example request
244 |
245 | ```curl
246 | $ curl https://api.backpulse.io/mysite/project/NzFVjUwig
247 | ```
248 |
249 | #### Example response
250 |
251 | ```json
252 | {
253 | "status": "success",
254 | "code": 200,
255 | "message": "success",
256 | "payload": {
257 | "short_id": "eS9u5RQmR",
258 | "site_name": "mysite",
259 | "title": string,
260 | "titles": []Translation,
261 | "descriptions": []Translation,
262 | "url": string,
263 | "created_at": Date,
264 | "updated_at": Date
265 | }
266 | }
267 | ```
268 |
269 | ### Contact
270 |
271 | Get contact informations.
272 |
273 | ```endpoint
274 | GET /contact
275 | ```
276 |
277 | #### Example request
278 |
279 | ```curl
280 | $ curl https://api.backpulse.io/mysite/contact
281 | ```
282 |
283 | #### Example response
284 |
285 | ```json
286 | {
287 | "status": "success",
288 | "code": 200,
289 | "message": "success",
290 | "payload": [
291 | {
292 | "site_name": "aureleoules",
293 | "name": "John Doe",
294 | "phone": "202-555-0199",
295 | "email": "contact@backpulse.io",
296 | "address": "355 Yukon Lane\nOak Park, MI 48237",
297 | "facebook_url": "https://facebook.com/...",
298 | "instagram_url": "https://instagram.com/...",
299 | "twitter_url": "https://twitter.com/...",
300 | "custom_fields": []CustomField
301 | }
302 | ]
303 | }
304 | ```
305 |
306 | ### About
307 | Get about informations.
308 |
309 | ```endpoint
310 | GET /about
311 | ```
312 |
313 | #### Example request
314 |
315 | ```curl
316 | $ curl https://api.backpulse.io/mysite/about
317 | ```
318 |
319 | #### Example response
320 |
321 | ```json
322 | {
323 | "status": "success",
324 | "code": 200,
325 | "message": "success",
326 | "payload": [
327 | {
328 | "site_name": "aureleoules",
329 | "name": "John Doe",
330 | "descriptions": []Translation,
331 | "titles": []Translation
332 | }
333 | ]
334 | }
335 | ```
--------------------------------------------------------------------------------
/database/sites.go:
--------------------------------------------------------------------------------
1 | package database
2 |
3 | import (
4 | "time"
5 |
6 | "github.com/backpulse/core/constants"
7 | "github.com/backpulse/core/models"
8 | "github.com/backpulse/core/utils"
9 | "gopkg.in/mgo.v2/bson"
10 | )
11 |
12 | // GetSiteTotalSize : return site size in MB
13 | func GetSiteTotalSize(siteID bson.ObjectId) float64 {
14 | photos, _ := GetSitePhotos(siteID)
15 |
16 | var totalSize float64
17 | for _, photo := range photos {
18 | totalSize = totalSize + photo.Size
19 | }
20 |
21 | files, _ := GetSiteFiles(siteID)
22 | for _, file := range files {
23 | totalSize = totalSize + file.Size
24 | }
25 |
26 | return totalSize
27 | }
28 |
29 | // TransferSite : Transfer the site to another user
30 | func TransferSite(site models.Site, lastOwner models.User, newOwner models.User) error {
31 | collections := []string{contactCollection, aboutCollection, galleriesCollection, projectsCollection, photosCollection}
32 | for _, c := range collections {
33 | DB.C(c).Update(bson.M{
34 | "site_id": site.ID,
35 | }, bson.M{
36 | "$set": bson.M{
37 | "owner_id": newOwner.ID,
38 | },
39 | })
40 | }
41 |
42 | err := DB.C(sitesCollection).UpdateId(site.ID, bson.M{
43 | "$set": bson.M{
44 | "owner_id": newOwner.ID,
45 | },
46 | })
47 |
48 | RemoveCollaborator(site.ID, newOwner.Email)
49 |
50 | AddCollaborator(site.ID, models.Collaborator{
51 | Email: lastOwner.Email,
52 | Role: "collaborator",
53 | })
54 |
55 | return err
56 | }
57 |
58 | // GetSitesOfUser : return array of Site of specific user
59 | func GetSitesOfUser(user models.User) ([]models.Site, error) {
60 | var sites []models.Site
61 | err := DB.C(sitesCollection).Find(bson.M{
62 | "$or": []bson.M{
63 | {
64 | "owner_id": user.ID,
65 | },
66 | {
67 | "collaborators": models.Collaborator{
68 | Email: user.Email,
69 | Role: "collaborator",
70 | },
71 | },
72 | },
73 | }).All(&sites)
74 |
75 | /* Dynamicly set favorite */
76 | for i := range sites {
77 | for _, id := range user.FavoriteSites {
78 | if sites[i].ID == id {
79 | sites[i].Favorite = true
80 | }
81 | }
82 | for _, collaborator := range sites[i].Collaborators {
83 | if collaborator.Email == user.Email {
84 | sites[i].Role = "collaborator"
85 | }
86 | }
87 | if sites[i].Role == "" {
88 | sites[i].Role = "owner"
89 | }
90 | }
91 |
92 | return sites, err
93 | }
94 |
95 | // GetOwnedSites : return only owned sites (and not the ones you might collaborate with)
96 | func GetOwnedSites(id bson.ObjectId) ([]models.Site, error) {
97 | var sites []models.Site
98 | err := DB.C(sitesCollection).Find(bson.M{
99 | "owner_id": id,
100 | }).All(&sites)
101 | return sites, err
102 | }
103 |
104 | // AddCollaborator : add collaborator to site
105 | func AddCollaborator(id bson.ObjectId, collaborator models.Collaborator) error {
106 | err := DB.C(sitesCollection).UpdateId(id, bson.M{
107 | "$push": bson.M{
108 | "collaborators": collaborator,
109 | },
110 | })
111 | return err
112 | }
113 |
114 | // RemoveCollaborator : remove collaborator from site
115 | func RemoveCollaborator(id bson.ObjectId, email string) error {
116 | err := DB.C(sitesCollection).UpdateId(id, bson.M{
117 | "$pull": bson.M{
118 | "collaborators": bson.M{
119 | "role": "collaborator",
120 | "email": email,
121 | }},
122 | })
123 | return err
124 | }
125 |
126 | // GetSiteByName : return specific site
127 | func GetSiteByName(name string) (models.Site, error) {
128 | var site models.Site
129 | err := DB.C(sitesCollection).Find(bson.M{
130 | "name": name,
131 | }).One(&site)
132 | return site, err
133 | }
134 |
135 | // GetSiteByID : return specific site by ObjectID
136 | func GetSiteByID(id bson.ObjectId) (models.Site, error) {
137 | var site models.Site
138 | err := DB.C(sitesCollection).FindId(id).One(&site)
139 | return site, err
140 | }
141 |
142 | // AddModule : add module to site
143 | func AddModule(site models.Site, module string) error {
144 | err := DB.C(sitesCollection).UpdateId(site.ID, bson.M{
145 | "$push": bson.M{
146 | "modules": constants.Module(module),
147 | },
148 | })
149 | return err
150 | }
151 |
152 | // RemoveModule : remove module from site
153 | func RemoveModule(site models.Site, module string) error {
154 | err := DB.C(sitesCollection).UpdateId(site.ID, bson.M{
155 | "$pull": bson.M{
156 | "modules": constants.Module(module),
157 | },
158 | })
159 |
160 | if err != nil {
161 | return err
162 | }
163 |
164 | //TODO: Remove data from other modules (videos, articles, music...)
165 | // Or ask user to delete it first idk
166 |
167 | if module == "galleries" {
168 | galleries, _ := GetGalleries(site.ID)
169 |
170 | for _, g := range galleries {
171 | photos, _ := GetGalleryPhotos(g.ID)
172 | utils.RemoveGoogleCloudPhotos(photos)
173 | }
174 | _, err := DB.C(photosCollection).RemoveAll(bson.M{
175 | "site_id": site.ID,
176 | })
177 | _, err = DB.C(galleriesCollection).RemoveAll(bson.M{
178 | "site_id": site.ID,
179 | })
180 | return err
181 | }
182 |
183 | if module == "projects" {
184 | _, err := DB.C(projectsCollection).RemoveAll(bson.M{
185 | "site_id": site.ID,
186 | })
187 | return err
188 | }
189 | //TODO remove articles
190 | return err
191 | }
192 |
193 | // UpdateSite : update site data
194 | func UpdateSite(id bson.ObjectId, data models.Site) error {
195 |
196 | err := DB.C(sitesCollection).UpdateId(id, bson.M{
197 | "$set": bson.M{
198 | "updated_at": time.Now(),
199 | "display_name": data.DisplayName,
200 | "name": data.Name,
201 | },
202 | })
203 | return err
204 | }
205 |
206 | // CreateSite : insert site in db
207 | func CreateSite(site models.Site) (models.Site, error) {
208 | site.CreatedAt = time.Now()
209 | site.UpdatedAt = time.Now()
210 | site.ID = bson.NewObjectId()
211 |
212 | err := DB.C(sitesCollection).Insert(site)
213 |
214 | UpdateContact(site.ID, models.ContactContent{OwnerID: site.OwnerID, SiteID: site.ID})
215 | UpdateAbout(site.ID, models.AboutContent{OwnerID: site.OwnerID, SiteID: site.ID})
216 |
217 | return site, err
218 | }
219 |
220 | // DeleteSite : complete erase
221 | func DeleteSite(site models.Site) error {
222 | collections := []string{contactCollection, aboutCollection, galleriesCollection, projectsCollection, photosCollection}
223 | for _, c := range collections {
224 | DB.C(c).RemoveAll(bson.M{
225 | "site_id": site.ID,
226 | })
227 | }
228 | return DB.C(sitesCollection).RemoveId(site.ID)
229 | }
230 |
231 | // SiteExists : check if site exists
232 | func SiteExists(name string) bool {
233 | count, _ := DB.C(sitesCollection).Find(bson.M{
234 | "name": name,
235 | }).Count()
236 | return count > 0
237 | }
238 |
239 | // SetSiteFavorite : set site favorite
240 | func SetSiteFavorite(user models.User, siteID bson.ObjectId) error {
241 | isFavorite := false
242 | for _, sID := range user.FavoriteSites {
243 | if siteID == sID {
244 | isFavorite = true
245 | }
246 | }
247 | if isFavorite {
248 | return DB.C(usersCollection).UpdateId(user.ID, bson.M{
249 | "$pull": bson.M{
250 | "favorite_sites": siteID,
251 | },
252 | })
253 | }
254 | return DB.C(usersCollection).UpdateId(user.ID, bson.M{
255 | "$push": bson.M{
256 | "favorite_sites": siteID,
257 | },
258 | })
259 | }
260 |
--------------------------------------------------------------------------------
/handlers/admin/galleries.go:
--------------------------------------------------------------------------------
1 | package adminhandlers
2 |
3 | import (
4 | "encoding/json"
5 | "log"
6 | "net/http"
7 | "time"
8 |
9 | "github.com/backpulse/core/database"
10 | "github.com/backpulse/core/models"
11 | "github.com/backpulse/core/utils"
12 | "github.com/gorilla/mux"
13 | "github.com/teris-io/shortid"
14 | "gopkg.in/mgo.v2/bson"
15 | )
16 |
17 | // DeleteGallery handler
18 | func DeleteGallery(w http.ResponseWriter, r *http.Request) {
19 | vars := mux.Vars(r)
20 | id := vars["id"]
21 |
22 | gallery, err := database.GetGallery(bson.ObjectIdHex(id))
23 | if err != nil {
24 | utils.RespondWithJSON(w, http.StatusNotFound, "not_found", nil)
25 | return
26 | }
27 |
28 | site, _ := database.GetSiteByID(gallery.SiteID)
29 | user, _ := database.GetUserByID(utils.GetUserObjectID(r))
30 |
31 | if !utils.IsAuthorized(site, user) {
32 | utils.RespondWithJSON(w, http.StatusUnauthorized, "unauthorized", nil)
33 | return
34 | }
35 |
36 | err = database.DeleteGallery(gallery.ID)
37 | if err != nil {
38 | utils.RespondWithJSON(w, http.StatusInternalServerError, "error", nil)
39 | }
40 | utils.RespondWithJSON(w, http.StatusOK, "success", nil)
41 | return
42 | }
43 |
44 | // GetGallery return gallery
45 | func GetGallery(w http.ResponseWriter, r *http.Request) {
46 | vars := mux.Vars(r)
47 | id := vars["id"]
48 |
49 | gallery, err := database.GetGallery(bson.ObjectIdHex(id))
50 | if err != nil {
51 | utils.RespondWithJSON(w, http.StatusNotFound, "not_found", nil)
52 | return
53 | }
54 | site, _ := database.GetSiteByID(gallery.SiteID)
55 | user, _ := database.GetUserByID(utils.GetUserObjectID(r))
56 |
57 | if !utils.IsAuthorized(site, user) {
58 | utils.RespondWithJSON(w, http.StatusUnauthorized, "unauthorized", nil)
59 | return
60 | }
61 |
62 | utils.RespondWithJSON(w, http.StatusOK, "success", gallery)
63 | return
64 | }
65 |
66 | // GetGalleries : return array of gallery
67 | func GetGalleries(w http.ResponseWriter, r *http.Request) {
68 | vars := mux.Vars(r)
69 | name := vars["name"]
70 |
71 | site, _ := database.GetSiteByName(name)
72 | user, _ := database.GetUserByID(utils.GetUserObjectID(r))
73 |
74 | if !utils.IsAuthorized(site, user) {
75 | utils.RespondWithJSON(w, http.StatusUnauthorized, "unauthorized", nil)
76 | return
77 | }
78 |
79 | galleries, err := database.GetGalleries(site.ID)
80 | if err != nil {
81 | utils.RespondWithJSON(w, http.StatusInternalServerError, "error", nil)
82 | return
83 | }
84 |
85 | utils.RespondWithJSON(w, http.StatusOK, "success", galleries)
86 | return
87 |
88 | }
89 |
90 | func SetGalleryPreview(w http.ResponseWriter, r *http.Request) {
91 | vars := mux.Vars(r)
92 | siteName := vars["name"]
93 | galleryID := vars["galleryID"]
94 | photoID := vars["id"]
95 |
96 | site, _ := database.GetSiteByName(siteName)
97 | user, _ := database.GetUserByID(utils.GetUserObjectID(r))
98 |
99 | if !utils.IsAuthorized(site, user) {
100 | utils.RespondWithJSON(w, http.StatusUnauthorized, "unauthorized", nil)
101 | return
102 | }
103 |
104 | gallery, err := database.GetGallery(bson.ObjectIdHex(galleryID))
105 | if err != nil {
106 | log.Print(err)
107 | utils.RespondWithJSON(w, http.StatusNotFound, "not_found", nil)
108 | return
109 | }
110 |
111 | if gallery.SiteID != site.ID {
112 | utils.RespondWithJSON(w, http.StatusUnauthorized, "unauthorized", nil)
113 | return
114 | }
115 |
116 | if !bson.IsObjectIdHex(photoID) {
117 | log.Print(err)
118 | utils.RespondWithJSON(w, http.StatusNotFound, "not_found", nil)
119 | return
120 | }
121 |
122 | photo, err := database.GetPhotoByID(bson.ObjectIdHex(photoID))
123 | if err != nil {
124 | log.Print(err)
125 | utils.RespondWithJSON(w, http.StatusNotFound, "not_found", nil)
126 | return
127 | }
128 | if photo.SiteID != site.ID || photo.GalleryID != &gallery.ID {
129 | utils.RespondWithJSON(w, http.StatusNotFound, "not_found", nil)
130 | return
131 | }
132 |
133 | err = database.SetGalleryPreview(gallery, photo)
134 | if err != nil {
135 | utils.RespondWithJSON(w, http.StatusInternalServerError, "error", nil)
136 | return
137 | }
138 |
139 | utils.RespondWithJSON(w, http.StatusOK, "success", nil)
140 | return
141 | }
142 |
143 | func SetDefaultGallery(w http.ResponseWriter, r *http.Request) {
144 | vars := mux.Vars(r)
145 | siteName := vars["name"]
146 | galleryID := vars["id"]
147 |
148 | site, _ := database.GetSiteByName(siteName)
149 | user, _ := database.GetUserByID(utils.GetUserObjectID(r))
150 |
151 | if !utils.IsAuthorized(site, user) {
152 | utils.RespondWithJSON(w, http.StatusUnauthorized, "unauthorized", nil)
153 | return
154 | }
155 |
156 | gallery, err := database.GetGallery(bson.ObjectIdHex(galleryID))
157 | if err != nil {
158 | utils.RespondWithJSON(w, http.StatusNotFound, "not_found", nil)
159 | return
160 | }
161 |
162 | if gallery.SiteID != site.ID {
163 | utils.RespondWithJSON(w, http.StatusUnauthorized, "unauthorized", nil)
164 | return
165 | }
166 |
167 | err = database.SetDefaultGallery(site, gallery)
168 | if err != nil {
169 | utils.RespondWithJSON(w, http.StatusInternalServerError, "error", nil)
170 | return
171 | }
172 |
173 | utils.RespondWithJSON(w, http.StatusOK, "success", nil)
174 | return
175 | }
176 |
177 | // UpdateGallery handler
178 | func UpdateGallery(w http.ResponseWriter, r *http.Request) {
179 | vars := mux.Vars(r)
180 | id := vars["id"]
181 |
182 | gallery, err := database.GetGallery(bson.ObjectIdHex(id))
183 | if err != nil {
184 | utils.RespondWithJSON(w, http.StatusNotFound, "not_found", nil)
185 | return
186 | }
187 | site, _ := database.GetSiteByID(gallery.SiteID)
188 | user, _ := database.GetUserByID(utils.GetUserObjectID(r))
189 |
190 | if !utils.IsAuthorized(site, user) {
191 | utils.RespondWithJSON(w, http.StatusUnauthorized, "unauthorized", nil)
192 | return
193 | }
194 |
195 | var galleryData models.Gallery
196 | /* Parse json to models.Gallery */
197 | err = json.NewDecoder(r.Body).Decode(&galleryData)
198 | if err != nil {
199 | utils.RespondWithJSON(w, http.StatusNotAcceptable, "error", nil)
200 | return
201 | }
202 |
203 | err = database.UpdateGallery(gallery.ID, galleryData)
204 | if err != nil {
205 | utils.RespondWithJSON(w, http.StatusInternalServerError, "error", nil)
206 | return
207 | }
208 | utils.RespondWithJSON(w, http.StatusOK, "success", nil)
209 | return
210 | }
211 |
212 | // CreateGallery handler
213 | func CreateGallery(w http.ResponseWriter, r *http.Request) {
214 | vars := mux.Vars(r)
215 | siteName := vars["name"]
216 | galleryName := vars["galleryName"]
217 |
218 | site, _ := database.GetSiteByName(siteName)
219 | user, _ := database.GetUserByID(utils.GetUserObjectID(r))
220 |
221 | if !utils.IsAuthorized(site, user) {
222 | utils.RespondWithJSON(w, http.StatusUnauthorized, "unauthorized", nil)
223 | return
224 | }
225 |
226 | if len(galleryName) > 150 {
227 | utils.RespondWithJSON(w, http.StatusNotAcceptable, "name_too_long", nil)
228 | return
229 | }
230 |
231 | if len(galleryName) < 1 {
232 | utils.RespondWithJSON(w, http.StatusNotAcceptable, "name_required", nil)
233 | return
234 | }
235 |
236 | galleries, _ := database.GetGalleries(site.ID)
237 |
238 | shortID, _ := shortid.Generate()
239 | gallery := models.Gallery{
240 | ID: bson.NewObjectId(),
241 | OwnerID: site.OwnerID,
242 | SiteID: site.ID,
243 | CreatedAt: time.Now(),
244 | UpdatedAt: time.Now(),
245 | ShortID: shortID,
246 | Index: len(galleries),
247 | Titles: []models.Translation{
248 | {
249 | Content: galleryName,
250 | LanguageCode: "en",
251 | LanguageName: "English",
252 | },
253 | },
254 | }
255 |
256 | err := database.CreateGallery(gallery)
257 | if err != nil {
258 | log.Println(err)
259 | utils.RespondWithJSON(w, http.StatusInternalServerError, "error", nil)
260 | return
261 | }
262 | utils.RespondWithJSON(w, http.StatusOK, "success", gallery)
263 | return
264 | }
265 |
266 | func UpdateGalleriesIndexes(w http.ResponseWriter, r *http.Request) {
267 | vars := mux.Vars(r)
268 | siteName := vars["name"]
269 |
270 | site, _ := database.GetSiteByName(siteName)
271 | user, _ := database.GetUserByID(utils.GetUserObjectID(r))
272 |
273 | if !utils.IsAuthorized(site, user) {
274 | utils.RespondWithJSON(w, http.StatusUnauthorized, "unauthorized", nil)
275 | return
276 | }
277 |
278 | var galleries []models.Gallery
279 | /* Parse json to models.Gallery */
280 | err := json.NewDecoder(r.Body).Decode(&galleries)
281 | if err != nil {
282 | utils.RespondWithJSON(w, http.StatusNotAcceptable, "error", nil)
283 | return
284 | }
285 |
286 | err = database.UpdateGalleriesIndexes(site.ID, galleries)
287 | if err != nil {
288 | utils.RespondWithJSON(w, http.StatusNotAcceptable, "error", nil)
289 | return
290 | }
291 | utils.RespondWithJSON(w, http.StatusOK, "success", nil)
292 | return
293 | }
294 |
--------------------------------------------------------------------------------
/constants/languages.go:
--------------------------------------------------------------------------------
1 | package constants
2 |
3 | type Language struct {
4 | Name string
5 | Code string
6 | }
7 |
8 | var Languages []Language = []Language{
9 | {
10 | Name: "Afar",
11 | Code: "aa",
12 | }, {
13 | Name: "Abkhazian",
14 | Code: "ab",
15 | }, {
16 | Name: "Avestan",
17 | Code: "ae",
18 | }, {
19 | Name: "Afrikaans",
20 | Code: "af",
21 | }, {
22 | Name: "Akan",
23 | Code: "ak",
24 | }, {
25 | Name: "Amharic",
26 | Code: "am",
27 | }, {
28 | Name: "Aragonese",
29 | Code: "an",
30 | }, {
31 | Name: "Arabic",
32 | Code: "ar",
33 | }, {
34 | Name: "Assamese",
35 | Code: "as",
36 | }, {
37 | Name: "Avaric",
38 | Code: "av",
39 | }, {
40 | Name: "Aymara",
41 | Code: "ay",
42 | }, {
43 | Name: "Azerbaijani",
44 | Code: "az",
45 | }, {
46 | Name: "Bashkir",
47 | Code: "ba",
48 | }, {
49 | Name: "Belarusian",
50 | Code: "be",
51 | }, {
52 | Name: "Bulgarian",
53 | Code: "bg",
54 | }, {
55 | Name: "Bihari",
56 | Code: "bh",
57 | }, {
58 | Name: "Bislama",
59 | Code: "bi",
60 | }, {
61 | Name: "Bambara",
62 | Code: "bm",
63 | }, {
64 | Name: "Bengali",
65 | Code: "bn",
66 | }, {
67 | Name: "Tibetan",
68 | Code: "bo",
69 | }, {
70 | Name: "Breton",
71 | Code: "br",
72 | }, {
73 | Name: "Bosnian",
74 | Code: "bs",
75 | }, {
76 | Name: "Catalan",
77 | Code: "ca",
78 | }, {
79 | Name: "Chechen",
80 | Code: "ce",
81 | }, {
82 | Name: "Chamorro",
83 | Code: "ch",
84 | }, {
85 | Name: "Corsican",
86 | Code: "co",
87 | }, {
88 | Name: "Cree",
89 | Code: "cr",
90 | }, {
91 | Name: "Czech",
92 | Code: "cs",
93 | }, {
94 | Name: "Church Slavic",
95 | Code: "cu",
96 | }, {
97 | Name: "Chuvash",
98 | Code: "cv",
99 | }, {
100 | Name: "Welsh",
101 | Code: "cy",
102 | }, {
103 | Name: "Danish",
104 | Code: "da",
105 | }, {
106 | Name: "German",
107 | Code: "de",
108 | }, {
109 | Name: "Divehi",
110 | Code: "dv",
111 | }, {
112 | Name: "Dzongkha",
113 | Code: "dz",
114 | }, {
115 | Name: "Ewe",
116 | Code: "ee",
117 | }, {
118 | Name: "Greek",
119 | Code: "el",
120 | }, {
121 | Name: "English",
122 | Code: "en",
123 | }, {
124 | Name: "Esperanto",
125 | Code: "eo",
126 | }, {
127 | Name: "Spanish",
128 | Code: "es",
129 | }, {
130 | Name: "Estonian",
131 | Code: "et",
132 | }, {
133 | Name: "Basque",
134 | Code: "eu",
135 | }, {
136 | Name: "Persian",
137 | Code: "fa",
138 | }, {
139 | Name: "Fulah",
140 | Code: "ff",
141 | }, {
142 | Name: "Finnish",
143 | Code: "fi",
144 | }, {
145 | Name: "Fijian",
146 | Code: "fj",
147 | }, {
148 | Name: "Faroese",
149 | Code: "fo",
150 | }, {
151 | Name: "French",
152 | Code: "fr",
153 | }, {
154 | Name: "Western Frisian",
155 | Code: "fy",
156 | }, {
157 | Name: "Irish",
158 | Code: "ga",
159 | }, {
160 | Name: "Gaelic",
161 | Code: "gd",
162 | }, {
163 | Name: "Galician",
164 | Code: "gl",
165 | }, {
166 | Name: "Guarani",
167 | Code: "gn",
168 | }, {
169 | Name: "Gujarati",
170 | Code: "gu",
171 | }, {
172 | Name: "Manx",
173 | Code: "gv",
174 | }, {
175 | Name: "Hausa",
176 | Code: "ha",
177 | }, {
178 | Name: "Hebrew",
179 | Code: "he",
180 | }, {
181 | Name: "Hindi",
182 | Code: "hi",
183 | }, {
184 | Name: "Hiri Motu",
185 | Code: "ho",
186 | }, {
187 | Name: "Croatian",
188 | Code: "hr",
189 | }, {
190 | Name: "Haitian",
191 | Code: "ht",
192 | }, {
193 | Name: "Hungarian",
194 | Code: "hu",
195 | }, {
196 | Name: "Armenian",
197 | Code: "hy",
198 | }, {
199 | Name: "Herero",
200 | Code: "hz",
201 | }, {
202 | Name: "Interlingua",
203 | Code: "ia",
204 | }, {
205 | Name: "Indonesian",
206 | Code: "id",
207 | }, {
208 | Name: "Interlingue",
209 | Code: "ie",
210 | }, {
211 | Name: "Igbo",
212 | Code: "ig",
213 | }, {
214 | Name: "Sichuan Yi",
215 | Code: "ii",
216 | }, {
217 | Name: "Inupiaq",
218 | Code: "ik",
219 | }, {
220 | Name: "Ido",
221 | Code: "io",
222 | }, {
223 | Name: "Icelandic",
224 | Code: "is",
225 | }, {
226 | Name: "Italian",
227 | Code: "it",
228 | }, {
229 | Name: "Inuktitut",
230 | Code: "iu",
231 | }, {
232 | Name: "Japanese",
233 | Code: "ja",
234 | }, {
235 | Name: "Javanese",
236 | Code: "jv",
237 | }, {
238 | Name: "Georgian",
239 | Code: "ka",
240 | }, {
241 | Name: "Kongo",
242 | Code: "kg",
243 | }, {
244 | Name: "Kikuyu",
245 | Code: "ki",
246 | }, {
247 | Name: "Kuanyama",
248 | Code: "kj",
249 | }, {
250 | Name: "Kazakh",
251 | Code: "kk",
252 | }, {
253 | Name: "Kalaallisut",
254 | Code: "kl",
255 | }, {
256 | Name: "Central Khmer",
257 | Code: "km",
258 | }, {
259 | Name: "Kannada",
260 | Code: "kn",
261 | }, {
262 | Name: "Korean",
263 | Code: "ko",
264 | }, {
265 | Name: "Kanuri",
266 | Code: "kr",
267 | }, {
268 | Name: "Kashmiri",
269 | Code: "ks",
270 | }, {
271 | Name: "Kurdish",
272 | Code: "ku",
273 | }, {
274 | Name: "Komi",
275 | Code: "kv",
276 | }, {
277 | Name: "Cornish",
278 | Code: "kw",
279 | }, {
280 | Name: "Kirghiz",
281 | Code: "ky",
282 | }, {
283 | Name: "Latin",
284 | Code: "la",
285 | }, {
286 | Name: "Luxembourgish",
287 | Code: "lb",
288 | }, {
289 | Name: "Ganda",
290 | Code: "lg",
291 | }, {
292 | Name: "Limburgan",
293 | Code: "li",
294 | }, {
295 | Name: "Lingala",
296 | Code: "ln",
297 | }, {
298 | Name: "Lao",
299 | Code: "lo",
300 | }, {
301 | Name: "Lithuanian",
302 | Code: "lt",
303 | }, {
304 | Name: "Luba-Katanga",
305 | Code: "lu",
306 | }, {
307 | Name: "Latvian",
308 | Code: "lv",
309 | }, {
310 | Name: "Malagasy",
311 | Code: "mg",
312 | }, {
313 | Name: "Marshallese",
314 | Code: "mh",
315 | }, {
316 | Name: "Maori",
317 | Code: "mi",
318 | }, {
319 | Name: "Macedonian",
320 | Code: "mk",
321 | }, {
322 | Name: "Malayalam",
323 | Code: "ml",
324 | }, {
325 | Name: "Mongolian",
326 | Code: "mn",
327 | }, {
328 | Name: "Marathi",
329 | Code: "mr",
330 | }, {
331 | Name: "Malay",
332 | Code: "ms",
333 | }, {
334 | Name: "Maltese",
335 | Code: "mt",
336 | }, {
337 | Name: "Burmese",
338 | Code: "my",
339 | }, {
340 | Name: "Nauru",
341 | Code: "na",
342 | }, {
343 | Name: "Bokmål",
344 | Code: "nb",
345 | }, {
346 | Name: "Ndebele",
347 | Code: "nd",
348 | }, {
349 | Name: "Nepali",
350 | Code: "ne",
351 | }, {
352 | Name: "Ndonga",
353 | Code: "ng",
354 | }, {
355 | Name: "Dutch",
356 | Code: "nl",
357 | }, {
358 | Name: "Norwegian Nynorsk",
359 | Code: "nn",
360 | }, {
361 | Name: "Norwegian",
362 | Code: "no",
363 | }, {
364 | Name: "Ndebele",
365 | Code: "nr",
366 | }, {
367 | Name: "Navajo",
368 | Code: "nv",
369 | }, {
370 | Name: "Chichewa",
371 | Code: "ny",
372 | }, {
373 | Name: "Occitan",
374 | Code: "oc",
375 | }, {
376 | Name: "Ojibwa",
377 | Code: "oj",
378 | }, {
379 | Name: "Oromo",
380 | Code: "om",
381 | }, {
382 | Name: "Oriya",
383 | Code: "or",
384 | }, {
385 | Name: "Ossetian",
386 | Code: "os",
387 | }, {
388 | Name: "Panjabi",
389 | Code: "pa",
390 | }, {
391 | Name: "Pali",
392 | Code: "pi",
393 | }, {
394 | Name: "Polish",
395 | Code: "pl",
396 | }, {
397 | Name: "Pushto",
398 | Code: "ps",
399 | }, {
400 | Name: "Portuguese",
401 | Code: "pt",
402 | }, {
403 | Name: "Quechua",
404 | Code: "qu",
405 | }, {
406 | Name: "Romansh",
407 | Code: "rm",
408 | }, {
409 | Name: "Rundi",
410 | Code: "rn",
411 | }, {
412 | Name: "Romanian",
413 | Code: "ro",
414 | }, {
415 | Name: "Russian",
416 | Code: "ru",
417 | }, {
418 | Name: "Kinyarwanda",
419 | Code: "rw",
420 | }, {
421 | Name: "Sanskrit",
422 | Code: "sa",
423 | }, {
424 | Name: "Sardinian",
425 | Code: "sc",
426 | }, {
427 | Name: "Sindhi",
428 | Code: "sd",
429 | }, {
430 | Name: "Northern Sami",
431 | Code: "se",
432 | }, {
433 | Name: "Sango",
434 | Code: "sg",
435 | }, {
436 | Name: "Sinhala",
437 | Code: "si",
438 | }, {
439 | Name: "Slovak",
440 | Code: "sk",
441 | }, {
442 | Name: "Slovenian",
443 | Code: "sl",
444 | }, {
445 | Name: "Samoan",
446 | Code: "sm",
447 | }, {
448 | Name: "Shona",
449 | Code: "sn",
450 | }, {
451 | Name: "Somali",
452 | Code: "so",
453 | }, {
454 | Name: "Albanian",
455 | Code: "sq",
456 | }, {
457 | Name: "Serbian",
458 | Code: "sr",
459 | }, {
460 | Name: "Swati",
461 | Code: "ss",
462 | }, {
463 | Name: "Sotho",
464 | Code: "st",
465 | }, {
466 | Name: "Sundanese",
467 | Code: "su",
468 | }, {
469 | Name: "Swedish",
470 | Code: "sv",
471 | }, {
472 | Name: "Swahili",
473 | Code: "sw",
474 | }, {
475 | Name: "Tamil",
476 | Code: "ta",
477 | }, {
478 | Name: "Telugu",
479 | Code: "te",
480 | }, {
481 | Name: "Tajik",
482 | Code: "tg",
483 | }, {
484 | Name: "Thai",
485 | Code: "th",
486 | }, {
487 | Name: "Tigrinya",
488 | Code: "ti",
489 | }, {
490 | Name: "Turkmen",
491 | Code: "tk",
492 | }, {
493 | Name: "Tagalog",
494 | Code: "tl",
495 | }, {
496 | Name: "Tswana",
497 | Code: "tn",
498 | }, {
499 | Name: "Tonga",
500 | Code: "to",
501 | }, {
502 | Name: "Turkish",
503 | Code: "tr",
504 | }, {
505 | Name: "Tsonga",
506 | Code: "ts",
507 | }, {
508 | Name: "Tatar",
509 | Code: "tt",
510 | }, {
511 | Name: "Twi",
512 | Code: "tw",
513 | }, {
514 | Name: "Tahitian",
515 | Code: "ty",
516 | }, {
517 | Name: "Uighur",
518 | Code: "ug",
519 | }, {
520 | Name: "Ukrainian",
521 | Code: "uk",
522 | }, {
523 | Name: "Urdu",
524 | Code: "ur",
525 | }, {
526 | Name: "Uzbek",
527 | Code: "uz",
528 | }, {
529 | Name: "Venda",
530 | Code: "ve",
531 | }, {
532 | Name: "Vietnamese",
533 | Code: "vi",
534 | }, {
535 | Name: "Volapük",
536 | Code: "vo",
537 | }, {
538 | Name: "Walloon",
539 | Code: "wa",
540 | }, {
541 | Name: "Wolof",
542 | Code: "wo",
543 | }, {
544 | Name: "Xhosa",
545 | Code: "xh",
546 | }, {
547 | Name: "Yiddish",
548 | Code: "yi",
549 | }, {
550 | Name: "Yoruba",
551 | Code: "yo",
552 | }, {
553 | Name: "Zhuang",
554 | Code: "za",
555 | }, {
556 | Name: "Chinese",
557 | Code: "zh",
558 | }, {
559 | Name: "Zulu",
560 | Code: "zu",
561 | },
562 | }
563 |
--------------------------------------------------------------------------------
/handlers/admin/photos.go:
--------------------------------------------------------------------------------
1 | package adminhandlers
2 |
3 | import (
4 | "encoding/json"
5 | "log"
6 | "math"
7 | "net/http"
8 | "strings"
9 | "time"
10 |
11 | "github.com/backpulse/core/database"
12 | "github.com/backpulse/core/models"
13 | "github.com/backpulse/core/utils"
14 | "github.com/gorilla/mux"
15 | "gopkg.in/mgo.v2/bson"
16 | )
17 |
18 | // UpdatePhotosIndexes : change order of photos
19 | func UpdatePhotosIndexes(w http.ResponseWriter, r *http.Request) {
20 | vars := mux.Vars(r)
21 | siteName := vars["name"]
22 | galleryID := vars["id"]
23 |
24 | site, _ := database.GetSiteByName(siteName)
25 | user, _ := database.GetUserByID(utils.GetUserObjectID(r))
26 |
27 | if !utils.IsAuthorized(site, user) {
28 | utils.RespondWithJSON(w, http.StatusUnauthorized, "unauthorized", nil)
29 | return
30 | }
31 |
32 | gallery, err := database.GetGallery(bson.ObjectIdHex(galleryID))
33 | if err != nil {
34 | utils.RespondWithJSON(w, http.StatusNotFound, "not_found", nil)
35 | return
36 | }
37 |
38 | if gallery.SiteID != site.ID {
39 | utils.RespondWithJSON(w, http.StatusUnauthorized, "unauthorized", nil)
40 | return
41 | }
42 |
43 | var photos []models.Photo
44 | /* Parse json to models.Gallery */
45 | err = json.NewDecoder(r.Body).Decode(&photos)
46 | if err != nil {
47 | utils.RespondWithJSON(w, http.StatusNotAcceptable, "error", nil)
48 | return
49 | }
50 |
51 | err = database.UpdatePhotosIndexes(gallery, photos)
52 | if err != nil {
53 | utils.RespondWithJSON(w, http.StatusInternalServerError, "error", nil)
54 | return
55 | }
56 | utils.RespondWithJSON(w, http.StatusOK, "success", nil)
57 | return
58 | }
59 |
60 | // DeletePhotos : remove photos from db & g cloud
61 | func DeletePhotos(w http.ResponseWriter, r *http.Request) {
62 | vars := mux.Vars(r)
63 | ids := vars["ids"]
64 | stringIDsArray := strings.Split(ids, ",")
65 | var array []bson.ObjectId
66 | log.Println(stringIDsArray)
67 | for _, id := range stringIDsArray {
68 | if bson.IsObjectIdHex(id) {
69 | array = append(array, bson.ObjectIdHex(id))
70 | }
71 | }
72 |
73 | log.Println(array)
74 |
75 | user, _ := database.GetUserByID(utils.GetUserObjectID(r))
76 |
77 | photos, err := database.GetPhotos(user.ID, array)
78 | if err != nil {
79 | utils.RespondWithJSON(w, http.StatusInternalServerError, "error", nil)
80 | return
81 | }
82 |
83 | var site models.Site
84 | for _, photo := range photos {
85 | if photo.SiteID != site.ID {
86 | site, _ = database.GetSiteByID(photo.SiteID)
87 | }
88 | if !utils.IsAuthorized(site, user) {
89 | utils.RespondWithJSON(w, http.StatusUnauthorized, "unauthorized", nil)
90 | return
91 | }
92 | }
93 |
94 | err = database.DeletePhotos(user.ID, array)
95 | if err != nil {
96 | utils.RespondWithJSON(w, http.StatusInternalServerError, "error", nil)
97 | return
98 | }
99 | err = utils.RemoveGoogleCloudPhotos(photos)
100 | // if err != nil {
101 | // utils.RespondWithJSON(w, http.StatusInternalServerError, "error", nil)
102 | // return
103 | // }
104 |
105 | utils.RespondWithJSON(w, http.StatusOK, "success", nil)
106 | return
107 | }
108 |
109 | // GetPhotos : return photos
110 | func GetPhotos(w http.ResponseWriter, r *http.Request) {
111 | vars := mux.Vars(r)
112 | siteName := vars["name"]
113 |
114 | site, _ := database.GetSiteByName(siteName)
115 | user, _ := database.GetUserByID(utils.GetUserObjectID(r))
116 |
117 | if !utils.IsAuthorized(site, user) {
118 | utils.RespondWithJSON(w, http.StatusUnauthorized, "unauthorized", nil)
119 | return
120 | }
121 |
122 | photos, err := database.GetSitePhotos(site.ID)
123 | if err != nil {
124 | utils.RespondWithJSON(w, http.StatusInternalServerError, "error", nil)
125 | return
126 | }
127 | utils.RespondWithJSON(w, http.StatusOK, "success", photos)
128 | return
129 | }
130 |
131 | func UpdatePhotoFile(w http.ResponseWriter, r *http.Request) {
132 | vars := mux.Vars(r)
133 | photoID := bson.ObjectIdHex(vars["id"])
134 | name := vars["name"]
135 | /* Get file from Client */
136 | image, header, err := r.FormFile("image")
137 | if err != nil {
138 | log.Println(err)
139 | utils.RespondWithJSON(w, http.StatusInternalServerError, err.Error(), nil)
140 | return
141 | }
142 | defer image.Close()
143 |
144 | if !strings.HasPrefix(header.Header.Get("Content-Type"), "image") {
145 | utils.RespondWithJSON(w, http.StatusNotAcceptable, "not_an_image", nil)
146 | return
147 | }
148 | site, _ := database.GetSiteByName(name)
149 | user, _ := database.GetUserByID(utils.GetUserObjectID(r))
150 |
151 | if !utils.IsAuthorized(site, user) {
152 | utils.RespondWithJSON(w, http.StatusUnauthorized, "unauthorized", nil)
153 | return
154 | }
155 |
156 | photo := models.Photo{
157 | Size: math.Round(float64(header.Size)/10000) / 100,
158 | Format: header.Header.Get("Content-Type"),
159 | }
160 |
161 | id, err := utils.UploadFile(image, header.Filename)
162 | if err != nil {
163 | log.Println(err)
164 | utils.RespondWithJSON(w, http.StatusInternalServerError, err.Error(), nil)
165 | return
166 | }
167 | config := utils.GetConfig()
168 | url := config.BucketPubURL + "/" + id.Hex()
169 |
170 | err = database.UpdatePhotoURL(photoID, url)
171 | if err != nil {
172 | utils.RespondWithJSON(w, http.StatusInternalServerError, err.Error(), nil)
173 | return
174 | }
175 |
176 | utils.RespondWithJSON(w, http.StatusOK, "success", photo)
177 | return
178 |
179 | }
180 |
181 | // UploadPhoto handler
182 | func UploadPhoto(w http.ResponseWriter, r *http.Request) {
183 | /* Get file from Client */
184 | image, header, err := r.FormFile("image")
185 | if err != nil {
186 | utils.RespondWithJSON(w, http.StatusInternalServerError, err.Error(), nil)
187 | return
188 | }
189 | defer image.Close()
190 |
191 | vars := mux.Vars(r)
192 | name := vars["name"]
193 |
194 | if !strings.HasPrefix(header.Header.Get("Content-Type"), "image") {
195 | utils.RespondWithJSON(w, http.StatusNotAcceptable, "not_an_image", nil)
196 | return
197 | }
198 | site, _ := database.GetSiteByName(name)
199 | user, _ := database.GetUserByID(utils.GetUserObjectID(r))
200 |
201 | if !utils.IsAuthorized(site, user) {
202 | utils.RespondWithJSON(w, http.StatusUnauthorized, "unauthorized", nil)
203 | return
204 | }
205 |
206 | photo := models.Photo{
207 | Size: math.Round(float64(header.Size)/10000) / 100,
208 | CreatedAt: time.Now(),
209 | OwnerID: site.OwnerID,
210 | Format: header.Header.Get("Content-Type"),
211 | SiteID: site.ID,
212 | }
213 |
214 | if r.FormValue("is_gallery") == "true" {
215 | // This is a gallery photos
216 | galleryID := r.FormValue("gallery_id")
217 | photo.IsGallery = true
218 | g, err := database.GetGallery(bson.ObjectIdHex(galleryID))
219 | if err != nil {
220 | utils.RespondWithJSON(w, http.StatusNotFound, "not_found", nil)
221 | return
222 | }
223 | if g.OwnerID != site.OwnerID {
224 | utils.RespondWithJSON(w, http.StatusUnauthorized, "unauthorized", nil)
225 | return
226 | }
227 | photo.GalleryID = &g.ID
228 | photo.Title = r.FormValue("title")
229 | photo.Content = r.FormValue("content")
230 |
231 | } else if r.FormValue("is_project") == "true" {
232 | // This is a project photo
233 | photo.IsProject = true
234 | projectID := r.FormValue("project_id")
235 |
236 | p, err := database.GetProject(bson.ObjectIdHex(projectID))
237 | if err != nil {
238 | utils.RespondWithJSON(w, http.StatusNotFound, "not_found", nil)
239 | return
240 | }
241 | if p.OwnerID != site.OwnerID {
242 | utils.RespondWithJSON(w, http.StatusUnauthorized, "unauthorized", nil)
243 | return
244 | }
245 | photo.ProjectID = p.ID
246 | } else {
247 | utils.RespondWithJSON(w, http.StatusBadRequest, "not_acceptable", nil)
248 | return
249 | }
250 |
251 | id, err := utils.UploadFile(image, header.Filename)
252 | if err != nil {
253 | utils.RespondWithJSON(w, http.StatusInternalServerError, err.Error(), nil)
254 | return
255 | }
256 | photo.ID = id
257 | config := utils.GetConfig()
258 | photo.URL = config.BucketPubURL + "/" + id.Hex()
259 |
260 | photo, err = database.InsertPhoto(photo)
261 | if err != nil {
262 | log.Println(err)
263 | utils.RespondWithJSON(w, http.StatusInternalServerError, "error", nil)
264 | return
265 | }
266 | utils.RespondWithJSON(w, http.StatusOK, "success", photo)
267 | return
268 | }
269 |
270 | func CreatePhoto(w http.ResponseWriter, r *http.Request) {
271 | vars := mux.Vars(r)
272 | siteName := vars["name"]
273 |
274 | site, _ := database.GetSiteByName(siteName)
275 | user, _ := database.GetUserByID(utils.GetUserObjectID(r))
276 |
277 | if !utils.IsAuthorized(site, user) {
278 | log.Println("unauthorized")
279 | utils.RespondWithJSON(w, http.StatusUnauthorized, "unauthorized", nil)
280 | return
281 | }
282 |
283 | var photo models.Photo
284 | /* Parse json to models.Project */
285 | err := json.NewDecoder(r.Body).Decode(&photo)
286 | if err != nil {
287 | utils.RespondWithJSON(w, http.StatusNotAcceptable, "error", nil)
288 | return
289 | }
290 |
291 | photo.OwnerID = site.OwnerID
292 | photo.SiteID = site.ID
293 |
294 | log.Println(photo)
295 |
296 | photo, err = database.CreatePhoto(photo)
297 | if err != nil {
298 | log.Println(err)
299 | utils.RespondWithJSON(w, http.StatusInternalServerError, "error", nil)
300 | return
301 | }
302 | utils.RespondWithJSON(w, http.StatusOK, "success", photo)
303 | return
304 |
305 | }
306 |
307 | func GetPhoto(w http.ResponseWriter, r *http.Request) {
308 | vars := mux.Vars(r)
309 | siteName := vars["name"]
310 | photoID := vars["id"]
311 |
312 | site, _ := database.GetSiteByName(siteName)
313 | user, _ := database.GetUserByID(utils.GetUserObjectID(r))
314 |
315 | if !utils.IsAuthorized(site, user) {
316 | utils.RespondWithJSON(w, http.StatusUnauthorized, "unauthorized", nil)
317 | return
318 | }
319 |
320 | photo, err := database.GetPhotoByID(bson.ObjectIdHex(photoID))
321 | if err != nil {
322 | utils.RespondWithJSON(w, http.StatusInternalServerError, "error", nil)
323 | return
324 | }
325 |
326 | if photo.GalleryID != nil {
327 | gallery, _ := database.GetGallery(*photo.GalleryID)
328 | photo.GalleryName = gallery.Title
329 | }
330 |
331 | utils.RespondWithJSON(w, http.StatusOK, "success", photo)
332 | return
333 | }
334 |
335 | func UpdatePhoto(w http.ResponseWriter, r *http.Request) {
336 | vars := mux.Vars(r)
337 | siteName := vars["name"]
338 | photoID := vars["id"]
339 |
340 | site, _ := database.GetSiteByName(siteName)
341 | user, _ := database.GetUserByID(utils.GetUserObjectID(r))
342 |
343 | if !utils.IsAuthorized(site, user) {
344 | utils.RespondWithJSON(w, http.StatusUnauthorized, "unauthorized", nil)
345 | return
346 | }
347 |
348 | var photo models.Photo
349 | /* Parse json to models.Project */
350 | err := json.NewDecoder(r.Body).Decode(&photo)
351 | if err != nil {
352 | log.Println(err)
353 | utils.RespondWithJSON(w, http.StatusNotAcceptable, "error", nil)
354 | return
355 | }
356 |
357 | err = database.UpdatePhoto(bson.ObjectIdHex(photoID), photo)
358 | if err != nil {
359 | log.Println(err)
360 | utils.RespondWithJSON(w, http.StatusInternalServerError, "error", nil)
361 | return
362 | }
363 | }
364 |
--------------------------------------------------------------------------------
/handlers/admin/users.go:
--------------------------------------------------------------------------------
1 | package adminhandlers
2 |
3 | import (
4 | "encoding/json"
5 | "log"
6 | "net/http"
7 | "strings"
8 | "time"
9 |
10 | "github.com/backpulse/core/database"
11 | "github.com/backpulse/core/models"
12 | "github.com/backpulse/core/utils"
13 | "github.com/gorilla/mux"
14 | "github.com/stripe/stripe-go"
15 | "golang.org/x/crypto/bcrypt"
16 | "gopkg.in/mgo.v2/bson"
17 | )
18 |
19 | // CreateUser : Handler for `POST /users`
20 | func CreateUser(w http.ResponseWriter, r *http.Request) {
21 | /* Parse JSON into models.User */
22 |
23 | var user models.User
24 | err := json.NewDecoder(r.Body).Decode(&user)
25 | if err != nil {
26 | utils.RespondWithJSON(w, http.StatusBadRequest, "error", nil)
27 | return
28 | }
29 |
30 | ok, errStr := database.VerifyEmail(user.Email, r)
31 | if !ok {
32 | utils.RespondWithJSON(w, http.StatusNotAcceptable, errStr, nil)
33 | return
34 | }
35 |
36 | /* Check password length */
37 | if len(user.Password) < 8 {
38 | utils.RespondWithJSON(w, http.StatusUnprocessableEntity, "password_too_short", nil)
39 | return
40 | }
41 | if len(user.Password) > 128 {
42 | utils.RespondWithJSON(w, http.StatusUnprocessableEntity, "password_too_long", nil)
43 | return
44 | }
45 |
46 | /* Hash password */
47 | password, _ := bcrypt.GenerateFromPassword([]byte(user.Password), 10)
48 | user.Password = string(password)
49 |
50 | /* Insert user in Database */
51 | user, err = database.AddUser(user)
52 | if err != nil {
53 | utils.RespondWithJSON(w, http.StatusInternalServerError, "error", nil)
54 | return
55 | }
56 |
57 | tokenString, err := utils.NewJWT(user, 48)
58 | if err != nil {
59 | utils.RespondWithJSON(w, http.StatusInternalServerError, "error", nil)
60 | return
61 | }
62 |
63 | /* Send token in payload */
64 | utils.RespondWithJSON(w, http.StatusOK, "success", tokenString)
65 |
66 | verification, _ := database.CreateVerification(user)
67 | err = utils.SendVerificationMail(user.Email, verification)
68 | if err != nil {
69 | log.Println(err)
70 | }
71 | return
72 | }
73 |
74 | // AuthenticateUser authenticates user
75 | func AuthenticateUser(w http.ResponseWriter, r *http.Request) {
76 | /* Load config (secret) */
77 |
78 | var data models.User
79 | /* Parse json to models.User */
80 | _ = json.NewDecoder(r.Body).Decode(&data)
81 |
82 | user, err := database.GetUser(data.Email)
83 | if err != nil {
84 | /* User wasn't found */
85 | utils.RespondWithJSON(w, http.StatusUnauthorized, "auth_fail", nil)
86 | return
87 | }
88 | err = bcrypt.CompareHashAndPassword([]byte(user.Password), []byte(data.Password))
89 | if err != nil {
90 | /* Password is not correct */
91 | utils.RespondWithJSON(w, http.StatusUnauthorized, "auth_fail", nil)
92 | return
93 | }
94 |
95 | /* Congratulation! You made it! Let's give a you a JWT */
96 | tokenString, err := utils.NewJWT(user, 48)
97 | if err != nil {
98 | utils.RespondWithJSON(w, http.StatusInternalServerError, "error", nil)
99 | return
100 | }
101 |
102 | /* Send token in payload */
103 | utils.RespondWithJSON(w, http.StatusOK, "success", tokenString)
104 | return
105 | }
106 |
107 | // GetSelfUser return self data
108 | func GetSelfUser(w http.ResponseWriter, r *http.Request) {
109 | id := utils.GetUserObjectID(r)
110 | user, err := database.GetUserByID(id)
111 |
112 | log.Println(err)
113 | if err != nil {
114 | utils.RespondWithJSON(w, http.StatusInternalServerError, "error", nil)
115 | return
116 | }
117 |
118 | user.Password = ""
119 | utils.RespondWithJSON(w, http.StatusOK, "success", user)
120 | return
121 | }
122 |
123 | func RemoveSubscription(w http.ResponseWriter, r *http.Request) {
124 | id := utils.GetUserObjectID(r)
125 | user, err := database.GetUserByID(id)
126 |
127 | _, err = utils.StripeClient.Subscriptions.Cancel(user.ActiveSubscriptionID, nil)
128 | if err != nil {
129 | utils.RespondWithJSON(w, http.StatusInternalServerError, "error", nil)
130 | return
131 | }
132 |
133 | err = database.RemoveUserSubscription(user)
134 | if err != nil {
135 | utils.RespondWithJSON(w, http.StatusInternalServerError, "error", nil)
136 | return
137 | }
138 |
139 | utils.RespondWithJSON(w, http.StatusOK, "success", nil)
140 | return
141 | }
142 |
143 | func ChargeAccount(w http.ResponseWriter, r *http.Request) {
144 |
145 | type chargeRequest struct {
146 | Name string `json:"name"`
147 | Address string `json:"address"`
148 | State string `json:"state"`
149 | ZIP string `json:"zip"`
150 | City string `json:"city"`
151 | Country string `json:"country"`
152 | Token stripe.Token `json:"token"`
153 | }
154 | var data chargeRequest
155 |
156 | id := utils.GetUserObjectID(r)
157 |
158 | user, err := database.GetUserByID(id)
159 | if err != nil {
160 | utils.RespondWithJSON(w, http.StatusInternalServerError, "error", nil)
161 | return
162 | }
163 |
164 | /* Parse json */
165 | _ = json.NewDecoder(r.Body).Decode(&data)
166 |
167 | if data.Address == "" {
168 | utils.RespondWithJSON(w, http.StatusNotAcceptable, "missing_address", nil)
169 | return
170 | }
171 | if data.City == "" {
172 | utils.RespondWithJSON(w, http.StatusNotAcceptable, "missing_city", nil)
173 | return
174 | }
175 | if data.State == "" {
176 | utils.RespondWithJSON(w, http.StatusNotAcceptable, "missing_state", nil)
177 | return
178 | }
179 | if data.ZIP == "" {
180 | utils.RespondWithJSON(w, http.StatusNotAcceptable, "missing_zip", nil)
181 | return
182 | }
183 | if data.Country == "" {
184 | utils.RespondWithJSON(w, http.StatusNotAcceptable, "missing_country", nil)
185 | return
186 | }
187 |
188 | if data.Name == "" {
189 | utils.RespondWithJSON(w, http.StatusNotAcceptable, "missing_name", nil)
190 | return
191 | }
192 |
193 | if data.Token.ID == "" {
194 | utils.RespondWithJSON(w, http.StatusNotAcceptable, "missing_token", nil)
195 | return
196 | }
197 |
198 | planID := "plan_EPMR80fLcjkok9"
199 |
200 | var customer *stripe.Customer
201 |
202 | if user.StripeID == "" {
203 | /* Didn't find any custom, let's create it */
204 | customerParams := &stripe.CustomerParams{
205 | Email: stripe.String(user.Email),
206 | Shipping: &stripe.CustomerShippingDetailsParams{
207 | Address: &stripe.AddressParams{
208 | City: stripe.String(data.City),
209 | Country: stripe.String(data.Country),
210 | Line1: stripe.String(data.Address),
211 | PostalCode: stripe.String(strings.Replace(data.ZIP, "_", "", -1)),
212 | State: stripe.String(data.State),
213 | },
214 | Name: stripe.String(data.Name),
215 | },
216 | }
217 | customerParams.SetSource(data.Token.ID)
218 |
219 | var err error
220 | customer, err = utils.StripeClient.Customers.New(customerParams)
221 | if err != nil {
222 | utils.RespondWithJSON(w, http.StatusInternalServerError, "error", nil)
223 | return
224 | }
225 | } else {
226 | customer, _ = utils.StripeClient.Customers.Get(user.StripeID, nil)
227 | if customer.Subscriptions.TotalCount > 0 {
228 | utils.RespondWithJSON(w, http.StatusNotAcceptable, "already_pro", nil)
229 | return
230 | }
231 | }
232 |
233 | subscription, err := utils.StripeClient.Subscriptions.New(&stripe.SubscriptionParams{
234 | Customer: stripe.String(customer.ID),
235 | Items: []*stripe.SubscriptionItemsParams{
236 | {
237 | Plan: stripe.String(planID),
238 | },
239 | },
240 | })
241 |
242 | if err != nil {
243 | utils.RespondWithJSON(w, http.StatusInternalServerError, "error", nil)
244 | return
245 | }
246 |
247 | err = database.SetUserPro(user.ID, customer, subscription)
248 | if err != nil {
249 | utils.RespondWithJSON(w, http.StatusInternalServerError, "error", nil)
250 | return
251 | }
252 |
253 | utils.RespondWithJSON(w, http.StatusOK, "success", nil)
254 | return
255 | }
256 |
257 | // UpdateUser : update self data
258 | func UpdateUser(w http.ResponseWriter, r *http.Request) {
259 |
260 | id := utils.GetUserObjectID(r)
261 |
262 | user, err := database.GetUserByID(id)
263 | if err != nil {
264 | utils.RespondWithJSON(w, http.StatusNotFound, "not_found", nil)
265 | }
266 |
267 | var userData models.User
268 | err = json.NewDecoder(r.Body).Decode(&userData)
269 | if err != nil {
270 | utils.RespondWithJSON(w, http.StatusBadRequest, "error", nil)
271 | return
272 | }
273 |
274 | if len(userData.FullName) > 35 {
275 | utils.RespondWithJSON(w, http.StatusNotAcceptable, "name_too_long", nil)
276 | return
277 | }
278 |
279 | if len(user.Country) > 100 {
280 | utils.RespondWithJSON(w, http.StatusNotAcceptable, "country_too_long", nil)
281 | return
282 | }
283 |
284 | if len(user.City) > 100 {
285 | utils.RespondWithJSON(w, http.StatusNotAcceptable, "city_too_long", nil)
286 | return
287 | }
288 |
289 | if len(user.Address) > 100 {
290 | utils.RespondWithJSON(w, http.StatusNotAcceptable, "address_too_long", nil)
291 | return
292 | }
293 | if len(user.ZIP) > 100 {
294 | utils.RespondWithJSON(w, http.StatusNotAcceptable, "zip_too_long", nil)
295 | return
296 | }
297 | if len(user.State) > 100 {
298 | utils.RespondWithJSON(w, http.StatusNotAcceptable, "state_too_long", nil)
299 | return
300 | }
301 |
302 | if user.Email != userData.Email {
303 | //Email is now different
304 | //We need to check multiple things
305 | ok, errStr := database.VerifyEmail(userData.Email, r)
306 | if !ok {
307 | utils.RespondWithJSON(w, http.StatusNotAcceptable, errStr, nil)
308 | return
309 | }
310 |
311 | verification, err := database.CreateVerification(models.User{
312 | ID: user.ID,
313 | Email: userData.Email,
314 | })
315 | if err != nil {
316 | utils.RespondWithJSON(w, http.StatusInternalServerError, "error", nil)
317 | return
318 | }
319 | err = utils.SendVerificationMail(userData.Email, verification)
320 | if err != nil {
321 | utils.RespondWithJSON(w, http.StatusInternalServerError, "error", nil)
322 | return
323 | }
324 |
325 | }
326 |
327 | newUser, err := database.UpdateUser(user, userData)
328 | if err != nil {
329 | utils.RespondWithJSON(w, http.StatusInternalServerError, "error", nil)
330 | return
331 | }
332 |
333 | tokenString, err := utils.NewJWT(newUser, 48)
334 | if err != nil {
335 | utils.RespondWithJSON(w, http.StatusInternalServerError, err.Error(), nil)
336 | return
337 | }
338 |
339 | /* Send new token in payload */
340 | utils.RespondWithJSON(w, http.StatusOK, "success", tokenString)
341 | return
342 | }
343 |
344 | // DeleteUser : remove user from db and also remove sheets & workbooks
345 | func DeleteUser(w http.ResponseWriter, r *http.Request) {
346 | id := utils.GetUserObjectID(r)
347 |
348 | sites, err := database.GetOwnedSites(id)
349 | if err != nil {
350 | utils.RespondWithJSON(w, http.StatusInternalServerError, "error", nil)
351 | return
352 | }
353 | if len(sites) > 0 {
354 | utils.RespondWithJSON(w, http.StatusNotAcceptable, "delete_sites_first", nil)
355 | return
356 | }
357 |
358 | err = database.RemoveUser(id)
359 | if err != nil {
360 | utils.RespondWithJSON(w, http.StatusInternalServerError, "error", nil)
361 | return
362 | }
363 | utils.RespondWithJSON(w, http.StatusOK, "success", nil)
364 | return
365 | }
366 |
367 | // UpdateUserPassword : update user password
368 | func UpdateUserPassword(w http.ResponseWriter, r *http.Request) {
369 | id := utils.GetUserObjectID(r)
370 |
371 | user, err := database.GetUserByID(id)
372 | if err != nil {
373 | utils.RespondWithJSON(w, http.StatusInternalServerError, "error", nil)
374 | return
375 | }
376 |
377 | type passwordChangeRequest struct {
378 | LastPassword string `json:"last_password"`
379 | NewPassword string `json:"new_password"`
380 | }
381 | var request passwordChangeRequest
382 | err = json.NewDecoder(r.Body).Decode(&request)
383 | if err != nil {
384 | utils.RespondWithJSON(w, http.StatusBadRequest, err.Error(), nil)
385 | return
386 | }
387 |
388 | /* Check if database's password hash is same as user request */
389 | err = bcrypt.CompareHashAndPassword([]byte(user.Password), []byte(request.LastPassword))
390 | if err != nil {
391 | utils.RespondWithJSON(w, http.StatusNotAcceptable, "wrong_last_password", nil)
392 | return
393 | }
394 |
395 | /* It is */
396 | /* Let's check if it a valid password */
397 | /* Now let's hash the "new password" */
398 |
399 | /* Check password length */
400 | if len(request.NewPassword) < 8 {
401 | utils.RespondWithJSON(w, http.StatusUnprocessableEntity, "password_too_short", nil)
402 | return
403 | }
404 | if len(request.NewPassword) > 128 {
405 | utils.RespondWithJSON(w, http.StatusUnprocessableEntity, "password_too_long", nil)
406 | return
407 | }
408 | newPasswordHash, _ := bcrypt.GenerateFromPassword([]byte(request.NewPassword), 10)
409 | newPasswordHashString := string(newPasswordHash)
410 |
411 | err = database.UpdateUserPassword(id, newPasswordHashString)
412 | if err != nil {
413 | utils.RespondWithJSON(w, http.StatusInternalServerError, "error", nil)
414 | return
415 | }
416 |
417 | utils.RespondWithJSON(w, http.StatusOK, "success", nil)
418 | return
419 | }
420 |
421 | // VerifyUser : verify user email
422 | func VerifyUser(w http.ResponseWriter, r *http.Request) {
423 | vars := mux.Vars(r)
424 | strID := vars["id"]
425 | if bson.IsObjectIdHex(strID) {
426 | id := bson.ObjectIdHex(strID)
427 | verification, err := database.GetVerificationByID(id)
428 | if err != nil {
429 | utils.RespondWithJSON(w, http.StatusNotFound, "not_found", nil)
430 | return
431 | }
432 | if time.Until(verification.ExpireAt) > 0 {
433 | //Verification didn't expire
434 | user, err := database.GetUserByID(verification.UserID)
435 | if err != nil {
436 | utils.RespondWithJSON(w, http.StatusNotFound, "not_found", nil)
437 | return
438 | }
439 | err = database.VerifyUser(user, verification)
440 | if err != nil {
441 | utils.RespondWithJSON(w, http.StatusInternalServerError, "error", nil)
442 | return
443 | }
444 |
445 | utils.RespondWithJSON(w, http.StatusOK, "success", nil)
446 | return
447 | }
448 | }
449 | }
450 |
--------------------------------------------------------------------------------
/handlers/admin/sites.go:
--------------------------------------------------------------------------------
1 | package adminhandlers
2 |
3 | import (
4 | "encoding/json"
5 | "log"
6 | "net/http"
7 | "regexp"
8 |
9 | "github.com/asaskevich/govalidator"
10 | "github.com/backpulse/core/constants"
11 | "github.com/backpulse/core/database"
12 | "github.com/backpulse/core/models"
13 | "github.com/backpulse/core/utils"
14 | "github.com/gorilla/mux"
15 | )
16 |
17 | // GetSites : return list of sites for a given user
18 | func GetSites(w http.ResponseWriter, r *http.Request) {
19 | id := utils.GetUserObjectID(r)
20 | user, _ := database.GetUserByID(id)
21 |
22 | sites, _ := database.GetSitesOfUser(user)
23 | utils.RespondWithJSON(w, http.StatusOK, "success", sites)
24 | return
25 | }
26 |
27 | // GetOverview : return site overview informations
28 | func GetOverview(w http.ResponseWriter, r *http.Request) {
29 | vars := mux.Vars(r)
30 | name := vars["name"]
31 |
32 | site, _ := database.GetSiteByName(name)
33 | user, _ := database.GetUserByID(utils.GetUserObjectID(r))
34 |
35 | if !utils.IsAuthorized(site, user) {
36 | utils.RespondWithJSON(w, http.StatusUnauthorized, "unauthorized", nil)
37 | return
38 | }
39 |
40 | for _, c := range site.Collaborators {
41 | if user.Email == c.Email {
42 | site.Role = "collaborator"
43 | }
44 | }
45 | if site.Role == "" {
46 | site.Role = "owner"
47 | }
48 |
49 | files, _ := database.GetSiteFiles(site.ID)
50 | galleries, _ := database.GetGalleries(site.ID)
51 | albums, _ := database.GetAlbums(site.ID)
52 | videogroups, _ := database.GetVideoGroups(site.ID)
53 | articles, _ := database.GetVideoGroups(site.ID)
54 | projects, _ := database.GetProjects(site.ID)
55 |
56 | type overview struct {
57 | Files int `json:"files"`
58 | Galleries int `json:"galleries"`
59 | Albums int `json:"albums"`
60 | VideoGroups int `json:"video_groups"`
61 | Articles int `json:"articles"`
62 | Projects int `json:"projects"`
63 |
64 | TotalSize float64 `json:"total_size"`
65 | Name string `json:"name"`
66 | DisplayName string `json:"display_name"`
67 | Collaborators []models.Collaborator `json:"collaborators"`
68 | Modules []constants.Module `json:"modules"`
69 | }
70 |
71 | data := overview{
72 | Files: len(files),
73 | Galleries: len(galleries),
74 | Albums: len(albums),
75 | VideoGroups: len(videogroups),
76 | Articles: len(articles),
77 | Projects: len(projects),
78 | DisplayName: site.DisplayName,
79 | Name: site.Name,
80 | Modules: site.Modules,
81 | Collaborators: site.Collaborators,
82 |
83 | TotalSize: database.GetSiteTotalSize(site.ID),
84 | }
85 |
86 | log.Println(database.GetSiteTotalSize(site.ID))
87 |
88 | utils.RespondWithJSON(w, http.StatusOK, "success", data)
89 | return
90 | }
91 |
92 | // GetSite : return specific site
93 | func GetSite(w http.ResponseWriter, r *http.Request) {
94 | vars := mux.Vars(r)
95 | name := vars["name"]
96 |
97 | site, _ := database.GetSiteByName(name)
98 | user, _ := database.GetUserByID(utils.GetUserObjectID(r))
99 |
100 | if !utils.IsAuthorized(site, user) {
101 | utils.RespondWithJSON(w, http.StatusUnauthorized, "unauthorized", nil)
102 | return
103 | }
104 |
105 | for _, c := range site.Collaborators {
106 | if user.Email == c.Email {
107 | site.Role = "collaborator"
108 | }
109 | }
110 | if site.Role == "" {
111 | site.Role = "owner"
112 | }
113 |
114 | site.TotalSize = database.GetSiteTotalSize(site.ID)
115 | utils.RespondWithJSON(w, http.StatusOK, "success", site)
116 | return
117 | }
118 |
119 | // RemoveModule : remove module from site & all data associated
120 | func RemoveModule(w http.ResponseWriter, r *http.Request) {
121 | vars := mux.Vars(r)
122 | name := vars["name"]
123 | moduleName := vars["module"]
124 | log.Println(moduleName)
125 |
126 | site, _ := database.GetSiteByName(name)
127 | user, _ := database.GetUserByID(utils.GetUserObjectID(r))
128 | if !utils.IsAuthorized(site, user) {
129 | utils.RespondWithJSON(w, http.StatusUnauthorized, "unauthorized", nil)
130 | return
131 | }
132 |
133 | err := database.RemoveModule(site, moduleName)
134 | if err != nil {
135 | utils.RespondWithJSON(w, http.StatusInternalServerError, "error", nil)
136 | return
137 | }
138 |
139 | exists := false
140 | for _, m := range site.Modules {
141 | if m == constants.Module(moduleName) {
142 | exists = true
143 | }
144 | }
145 | if !exists {
146 | utils.RespondWithJSON(w, http.StatusNotFound, "not_found", nil)
147 | return
148 | }
149 |
150 | utils.RespondWithJSON(w, http.StatusOK, "success", nil)
151 | return
152 | }
153 |
154 | func AddModule(w http.ResponseWriter, r *http.Request) {
155 | vars := mux.Vars(r)
156 | name := vars["name"]
157 | moduleName := vars["module"]
158 | log.Println(moduleName)
159 |
160 | site, _ := database.GetSiteByName(name)
161 | user, _ := database.GetUserByID(utils.GetUserObjectID(r))
162 | if !utils.IsAuthorized(site, user) {
163 | utils.RespondWithJSON(w, http.StatusUnauthorized, "unauthorized", nil)
164 | return
165 | }
166 |
167 | /* Check if module exists */
168 | exists := utils.CheckModuleExists(moduleName)
169 | if !exists {
170 | utils.RespondWithJSON(w, http.StatusNotFound, "not_found", nil)
171 | return
172 | }
173 |
174 | /* Check if module has not already been added */
175 | for _, m := range site.Modules {
176 | if m == constants.Module(moduleName) {
177 | utils.RespondWithJSON(w, http.StatusConflict, "exists", nil)
178 | return
179 | }
180 | }
181 |
182 | err := database.AddModule(site, moduleName)
183 | if err != nil {
184 | utils.RespondWithJSON(w, http.StatusInternalServerError, "error", nil)
185 | return
186 | }
187 | utils.RespondWithJSON(w, http.StatusOK, "success", nil)
188 | return
189 |
190 | }
191 |
192 | func GetSiteModules(w http.ResponseWriter, r *http.Request) {
193 | vars := mux.Vars(r)
194 | name := vars["name"]
195 |
196 | site, _ := database.GetSiteByName(name)
197 | user, _ := database.GetUserByID(utils.GetUserObjectID(r))
198 | if !utils.IsAuthorized(site, user) {
199 | utils.RespondWithJSON(w, http.StatusUnauthorized, "unauthorized", nil)
200 | return
201 | }
202 |
203 | utils.RespondWithJSON(w, http.StatusOK, "success", site.Modules)
204 | return
205 | }
206 |
207 | // Favorite : favorite a site
208 | func Favorite(w http.ResponseWriter, r *http.Request) {
209 | vars := mux.Vars(r)
210 | name := vars["name"]
211 |
212 | site, _ := database.GetSiteByName(name)
213 | user, _ := database.GetUserByID(utils.GetUserObjectID(r))
214 | if !utils.IsAuthorized(site, user) {
215 | utils.RespondWithJSON(w, http.StatusUnauthorized, "unauthorized", nil)
216 | return
217 | }
218 | database.SetSiteFavorite(user, site.ID)
219 |
220 | utils.RespondWithJSON(w, http.StatusOK, "success", nil)
221 | return
222 | }
223 |
224 | // DeleteSite : remove all galleries, projects, photos, about, contact
225 | func DeleteSite(w http.ResponseWriter, r *http.Request) {
226 | vars := mux.Vars(r)
227 | name := vars["name"]
228 | id := utils.GetUserObjectID(r)
229 | site, _ := database.GetSiteByName(name)
230 | if site.OwnerID != id {
231 | utils.RespondWithJSON(w, http.StatusUnauthorized, "unauthorized", nil)
232 | return
233 | }
234 |
235 | // Delete photos
236 |
237 | galleries, _ := database.GetGalleries(site.ID)
238 | for _, g := range galleries {
239 | photos, _ := database.GetGalleryPhotos(g.ID)
240 | utils.RemoveGoogleCloudPhotos(photos)
241 | }
242 |
243 | err := database.DeleteSite(site)
244 | if err != nil {
245 | utils.RespondWithJSON(w, http.StatusInternalServerError, "error", nil)
246 | return
247 | }
248 | utils.RespondWithJSON(w, http.StatusOK, "success", nil)
249 | return
250 | }
251 |
252 | func GetCollaborators(w http.ResponseWriter, r *http.Request) {
253 | vars := mux.Vars(r)
254 | name := vars["name"]
255 |
256 | site, _ := database.GetSiteByName(name)
257 | user, _ := database.GetUserByID(utils.GetUserObjectID(r))
258 | if !utils.IsAuthorized(site, user) {
259 | utils.RespondWithJSON(w, http.StatusUnauthorized, "unauthorized", nil)
260 | return
261 | }
262 |
263 | owner, _ := database.GetUserByID(site.OwnerID)
264 | site.Collaborators = append(site.Collaborators, models.Collaborator{
265 | Email: owner.Email,
266 | Role: "owner",
267 | })
268 |
269 | utils.RespondWithJSON(w, http.StatusOK, "success", site.Collaborators)
270 | return
271 | }
272 |
273 | func AddCollaborator(w http.ResponseWriter, r *http.Request) {
274 | vars := mux.Vars(r)
275 | name := vars["name"]
276 | email := vars["email"]
277 |
278 | id := utils.GetUserObjectID(r)
279 | site, _ := database.GetSiteByName(name)
280 | if site.OwnerID != id {
281 | utils.RespondWithJSON(w, http.StatusUnauthorized, "unauthorized", nil)
282 | return
283 | }
284 |
285 | if !govalidator.IsEmail(email) {
286 | utils.RespondWithJSON(w, http.StatusNotAcceptable, "incorrect_email", nil)
287 | return
288 | }
289 |
290 | user, _ := database.GetUserByID(site.OwnerID)
291 | if email == user.Email {
292 | utils.RespondWithJSON(w, http.StatusConflict, "exists", nil)
293 | return
294 | }
295 |
296 | /* Check if email already exists */
297 | for _, c := range site.Collaborators {
298 | if c.Email == email {
299 | utils.RespondWithJSON(w, http.StatusConflict, "exists", nil)
300 | return
301 | }
302 | }
303 |
304 | owner, err := database.GetUserByID(site.OwnerID)
305 | if err != nil {
306 | utils.RespondWithJSON(w, http.StatusInternalServerError, "error", nil)
307 | return
308 | }
309 |
310 | if len(site.Collaborators) > 0 && !owner.Professional {
311 | utils.RespondWithJSON(w, http.StatusNotAcceptable, "upgrade_account", nil)
312 | return
313 | }
314 |
315 | err = database.AddCollaborator(site.ID, models.Collaborator{
316 | Email: email,
317 | Role: "collaborator",
318 | })
319 | if err != nil {
320 | utils.RespondWithJSON(w, http.StatusInternalServerError, "error", nil)
321 | return
322 | }
323 | utils.RespondWithJSON(w, http.StatusOK, "success", nil)
324 | return
325 |
326 | }
327 |
328 | func TransferSite(w http.ResponseWriter, r *http.Request) {
329 | vars := mux.Vars(r)
330 | name := vars["name"]
331 | email := vars["email"]
332 |
333 | id := utils.GetUserObjectID(r)
334 | site, _ := database.GetSiteByName(name)
335 | if site.OwnerID != id {
336 | utils.RespondWithJSON(w, http.StatusUnauthorized, "unauthorized", nil)
337 | return
338 | }
339 |
340 | exists := false
341 | for _, c := range site.Collaborators {
342 | if c.Email == email {
343 | exists = true
344 | break
345 | }
346 | }
347 | if !exists {
348 | utils.RespondWithJSON(w, http.StatusNotFound, "not_found", nil)
349 | return
350 | }
351 |
352 | isPremium := utils.HasPremiumFeatures(site, database.GetSiteTotalSize(site.ID))
353 |
354 | nextOwner, err := database.GetUserByEmail(email)
355 | if err != nil {
356 | utils.RespondWithJSON(w, http.StatusNotFound, "not_found", nil)
357 | return
358 | }
359 | if isPremium && !nextOwner.Professional {
360 | utils.RespondWithJSON(w, http.StatusNotAcceptable, "upgrade_account", nil)
361 | return
362 | }
363 |
364 | lastOwner, err := database.GetUserByID(site.OwnerID)
365 | if err != nil {
366 | utils.RespondWithJSON(w, http.StatusInternalServerError, "error", nil)
367 | return
368 | }
369 |
370 | err = database.TransferSite(site, lastOwner, nextOwner)
371 | if err != nil {
372 | log.Println(err)
373 | utils.RespondWithJSON(w, http.StatusInternalServerError, "error", nil)
374 | return
375 | }
376 |
377 | utils.RespondWithJSON(w, http.StatusOK, "success", nil)
378 | return
379 | }
380 |
381 | func RemoveCollaborator(w http.ResponseWriter, r *http.Request) {
382 | vars := mux.Vars(r)
383 | name := vars["name"]
384 | email := vars["email"]
385 |
386 | id := utils.GetUserObjectID(r)
387 | site, _ := database.GetSiteByName(name)
388 | if site.OwnerID != id {
389 | utils.RespondWithJSON(w, http.StatusUnauthorized, "unauthorized", nil)
390 | return
391 | }
392 |
393 | if !govalidator.IsEmail(email) {
394 | utils.RespondWithJSON(w, http.StatusNotAcceptable, "incorrect_email", nil)
395 | return
396 | }
397 |
398 | user, _ := database.GetUserByID(site.OwnerID)
399 | if email == user.Email {
400 | utils.RespondWithJSON(w, http.StatusUnauthorized, "unauthorized", nil)
401 | return
402 | }
403 |
404 | exists := false
405 | for _, c := range site.Collaborators {
406 | if c.Email == email {
407 | exists = true
408 | }
409 | }
410 | if !exists {
411 | utils.RespondWithJSON(w, http.StatusNotAcceptable, "not_found", nil)
412 | return
413 | }
414 |
415 | err := database.RemoveCollaborator(site.ID, email)
416 | if err != nil {
417 | utils.RespondWithJSON(w, http.StatusInternalServerError, "error", nil)
418 | return
419 | }
420 | utils.RespondWithJSON(w, http.StatusOK, "success", nil)
421 | return
422 | }
423 |
424 | // UpdateSite : update site informations
425 | func UpdateSite(w http.ResponseWriter, r *http.Request) {
426 | var siteData models.Site
427 | /* Parse json to models.User */
428 | err := json.NewDecoder(r.Body).Decode(&siteData)
429 | if err != nil {
430 | utils.RespondWithJSON(w, http.StatusNotAcceptable, "error", nil)
431 | return
432 | }
433 | vars := mux.Vars(r)
434 | name := vars["name"]
435 |
436 | id := utils.GetUserObjectID(r)
437 | site, _ := database.GetSiteByName(name)
438 | if site.OwnerID != id {
439 | utils.RespondWithJSON(w, http.StatusUnauthorized, "unauthorized", nil)
440 | return
441 | }
442 |
443 | if site.Name != siteData.Name {
444 | if len(siteData.Name) < 3 {
445 | utils.RespondWithJSON(w, http.StatusNotAcceptable, "name_too_short", nil)
446 | return
447 | }
448 | if len(siteData.Name) > 30 {
449 | utils.RespondWithJSON(w, http.StatusNotAcceptable, "name_too_long", nil)
450 | return
451 | }
452 |
453 | /* lowercase letters, dashes, digits */
454 | var re = regexp.MustCompile(`([a-z-\d]+)`)
455 | var str = siteData.Name
456 |
457 | name := re.FindString(str)
458 | if name != siteData.Name {
459 | utils.RespondWithJSON(w, http.StatusNotAcceptable, "incorrect_characters", nil)
460 | return
461 | }
462 |
463 | /* Check if site doesn't already exists */
464 | exists := database.SiteExists(siteData.Name)
465 | if exists {
466 | utils.RespondWithJSON(w, http.StatusConflict, "site_exists", nil)
467 | return
468 | }
469 | }
470 |
471 | if len(siteData.DisplayName) > 60 {
472 | utils.RespondWithJSON(w, http.StatusNotAcceptable, "display_name_too_long", nil)
473 | return
474 | }
475 |
476 | err = database.UpdateSite(site.ID, siteData)
477 | if err != nil {
478 | utils.RespondWithJSON(w, http.StatusInternalServerError, "error", nil)
479 | return
480 | }
481 | utils.RespondWithJSON(w, http.StatusOK, "success", nil)
482 | return
483 |
484 | }
485 |
486 | // CreateSite : add site to db
487 | func CreateSite(w http.ResponseWriter, r *http.Request) {
488 | var site models.Site
489 | /* Parse json to models.User */
490 | err := json.NewDecoder(r.Body).Decode(&site)
491 | if err != nil {
492 | utils.RespondWithJSON(w, http.StatusNotAcceptable, "error", nil)
493 | return
494 | }
495 |
496 | if len(site.Name) < 3 {
497 | utils.RespondWithJSON(w, http.StatusNotAcceptable, "name_too_short", nil)
498 | return
499 | }
500 | if len(site.Name) > 30 {
501 | utils.RespondWithJSON(w, http.StatusNotAcceptable, "name_too_long", nil)
502 | return
503 | }
504 |
505 | /* lowercase letters, dashes, digits */
506 | var re = regexp.MustCompile(`([a-z-\d]+)`)
507 | var str = site.Name
508 |
509 | name := re.FindString(str)
510 | if name != site.Name {
511 | utils.RespondWithJSON(w, http.StatusNotAcceptable, "incorrect_characters", nil)
512 | return
513 | }
514 |
515 | /* Check if site doesn't already exists */
516 | exists := database.SiteExists(site.Name)
517 | if exists {
518 | utils.RespondWithJSON(w, http.StatusConflict, "site_exists", nil)
519 | return
520 | }
521 |
522 | /* Looks good */
523 |
524 | id := utils.GetUserObjectID(r)
525 |
526 | site.OwnerID = id
527 | site.DisplayName = site.Name
528 |
529 | site, err = database.CreateSite(site)
530 | log.Print(err)
531 | if err != nil {
532 | utils.RespondWithJSON(w, http.StatusInternalServerError, "error", nil)
533 | return
534 | }
535 | utils.RespondWithJSON(w, http.StatusOK, "success", site)
536 | return
537 | }
538 |
--------------------------------------------------------------------------------