mirror of
https://github.com/gmemstr/pogo.git
synced 2024-09-20 01:31:09 +01:00
Reove user DB from repo
This commit is contained in:
commit
6f2981a2be
4
.gitignore
vendored
4
.gitignore
vendored
|
@ -22,3 +22,7 @@ feed\.rss
|
||||||
vendor/
|
vendor/
|
||||||
|
|
||||||
assets/config/users\.db
|
assets/config/users\.db
|
||||||
|
|
||||||
|
run\.lockfile
|
||||||
|
|
||||||
|
\.lock
|
||||||
|
|
8
Godeps/Godeps.json
generated
8
Godeps/Godeps.json
generated
|
@ -8,6 +8,14 @@
|
||||||
"Comment": "v1.4.2-6-g4da3e2c",
|
"Comment": "v1.4.2-6-g4da3e2c",
|
||||||
"Rev": "4da3e2cfbabc9f751898f250b49f2439785783a1"
|
"Rev": "4da3e2cfbabc9f751898f250b49f2439785783a1"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"ImportPath": "github.com/google/go-github/github",
|
||||||
|
"Rev": "fbfee053c26dab3772adfc7799d995eed379133e"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ImportPath": "github.com/google/go-querystring/query",
|
||||||
|
"Rev": "53e6ce116135b80d037921a7fdd5138cf32d7a8a"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "github.com/gorilla/feeds",
|
"ImportPath": "github.com/gorilla/feeds",
|
||||||
"Comment": "v1.0.0",
|
"Comment": "v1.0.0",
|
||||||
|
|
|
@ -8,7 +8,7 @@ Podcast RSS feed generator and CMS in Go.
|
||||||
|
|
||||||
There are a couple options for getting Pogo up and running.
|
There are a couple options for getting Pogo up and running.
|
||||||
|
|
||||||
- [Download the latest release](https://github.com/gmemstr/pogo/releases/latest)
|
- [Download the latest release](#running)
|
||||||
- [Clone the repo and build](#building)
|
- [Clone the repo and build](#building)
|
||||||
|
|
||||||
## Status
|
## Status
|
||||||
|
@ -29,11 +29,10 @@ There are a couple options for getting Pogo up and running.
|
||||||
|
|
||||||
1. [Download the latest release](https://github.com/gmemstr/pogo/releases/latest)
|
1. [Download the latest release](https://github.com/gmemstr/pogo/releases/latest)
|
||||||
2. Unzip somewhere safe
|
2. Unzip somewhere safe
|
||||||
3. [Edit the config](https://github.com/gmemstr/pogo/wiki/Configuration)
|
3. [Edit the configuration](https://github.com/gmemstr/pogo/wiki/Configuration)
|
||||||
4. Run `pogo`
|
4. Run `pogo`
|
||||||
5. Navigate to your instance (`localhost:3000` by default)
|
5. Navigate to your instance (`localhost:3000` by default)
|
||||||
6. Login to the admin interface (default: **admin**, **password1**)
|
6. Login to the admin interface (your credentials are generated on the first run)
|
||||||
7. **CHANGE YOUR PASSWORD**
|
|
||||||
|
|
||||||
## Building
|
## Building
|
||||||
|
|
||||||
|
|
Binary file not shown.
|
@ -10,7 +10,7 @@
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<div id="app">
|
<div id="app">
|
||||||
<nav>
|
<nav>
|
||||||
<router-link to="/publish">Publish</router-link> <router-link to="/manage">Episodes</router-link> <router-link to="/theme">Theme</router-link> <router-link to="/users">Users</router-link></nav>
|
<router-link to="/publish">Publish</router-link> <router-link to="/manage">Episodes</router-link> <router-link to="/theme">Theme</router-link> <router-link to="/users">Users</router-link> <button onclick="logout()">Logout</button></nav>
|
||||||
<h1>{{ header }}</h1>
|
<h1>{{ header }}</h1>
|
||||||
<router-view></router-view>
|
<router-view></router-view>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,30 +0,0 @@
|
||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en">
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8">
|
|
||||||
<title>Pogo Setup</title>
|
|
||||||
<link rel="stylesheet" href="/assets/setup.css">
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<h1>Let's get Pogo setup</h1>
|
|
||||||
|
|
||||||
<form action="setup" method="post" class="setupform">
|
|
||||||
|
|
||||||
<label for="podcastname">Podcast Name</label>
|
|
||||||
<input type="text" id="podcastname" name="podcastname">
|
|
||||||
|
|
||||||
<label for="podcasthost">Podcast Host</label>
|
|
||||||
<input type="text" id="podcasthost" name="podcasthost">
|
|
||||||
|
|
||||||
<label for="podcastemail">Podcast Email</label>
|
|
||||||
<input type="text" id="podcastemail" name="podcastemail">
|
|
||||||
|
|
||||||
<label for="podcastdescription">Podcast Description</label>
|
|
||||||
<textarea name="" id="podcastdescription" name="podcastdescription" cols="75" rows="5"></textarea>
|
|
||||||
|
|
||||||
<input type="submit" value="Submit">
|
|
||||||
|
|
||||||
</form>
|
|
||||||
|
|
||||||
</body>
|
|
||||||
</html>
|
|
|
@ -377,3 +377,8 @@ function get(url,callback) {
|
||||||
xmlHttp.open("GET", url, true);
|
xmlHttp.open("GET", url, true);
|
||||||
xmlHttp.send(null);
|
xmlHttp.send(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function logout() {
|
||||||
|
document.cookie = "POGO_SESSION=;expires=Thu, 01 Jan 1970 00:00:01 GMT";
|
||||||
|
window.location = "/";
|
||||||
|
}
|
|
@ -2,13 +2,10 @@ package router
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"database/sql"
|
"database/sql"
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"golang.org/x/crypto/bcrypt"
|
"golang.org/x/crypto/bcrypt"
|
||||||
"io/ioutil"
|
|
||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strings"
|
|
||||||
|
|
||||||
_ "github.com/mattn/go-sqlite3"
|
_ "github.com/mattn/go-sqlite3"
|
||||||
|
|
||||||
|
@ -52,6 +49,7 @@ func Init() *mux.Router {
|
||||||
|
|
||||||
// "Static" paths
|
// "Static" paths
|
||||||
r.PathPrefix("/assets/").Handler(http.StripPrefix("/assets/", http.FileServer(http.Dir("assets/web/static"))))
|
r.PathPrefix("/assets/").Handler(http.StripPrefix("/assets/", http.FileServer(http.Dir("assets/web/static"))))
|
||||||
|
r.PathPrefix("/static/").Handler(http.StripPrefix("/static/", http.FileServer(http.Dir("assets/web/static"))))
|
||||||
r.PathPrefix("/download/").Handler(http.StripPrefix("/download/", http.FileServer(http.Dir("podcasts"))))
|
r.PathPrefix("/download/").Handler(http.StripPrefix("/download/", http.FileServer(http.Dir("podcasts"))))
|
||||||
|
|
||||||
// Paths that require specific handlers
|
// Paths that require specific handlers
|
||||||
|
@ -122,10 +120,6 @@ func Init() *mux.Router {
|
||||||
admin.ListUsers(),
|
admin.ListUsers(),
|
||||||
)).Methods("GET")
|
)).Methods("GET")
|
||||||
|
|
||||||
r.Handle("/setup", Handle(
|
|
||||||
serveSetup(),
|
|
||||||
)).Methods("GET", "POST")
|
|
||||||
|
|
||||||
return r
|
return r
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -164,9 +158,9 @@ func loginHandler() common.Handler {
|
||||||
password := r.Form.Get("password")
|
password := r.Form.Get("password")
|
||||||
rows, err := statement.Query(username)
|
rows, err := statement.Query(username)
|
||||||
|
|
||||||
if username == "" || password == "" {
|
if username == "" || password == "" || err != nil {
|
||||||
return &common.HTTPError{
|
return &common.HTTPError{
|
||||||
Message: "username or password is empty",
|
Message: "username or password is invalid",
|
||||||
StatusCode: http.StatusBadRequest,
|
StatusCode: http.StatusBadRequest,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -187,7 +181,7 @@ func loginHandler() common.Handler {
|
||||||
|
|
||||||
}
|
}
|
||||||
// Create a cookie here because the credentials are correct
|
// Create a cookie here because the credentials are correct
|
||||||
if dbun == username && bcrypt.CompareHashAndPassword([]byte(dbhsh), []byte(password)) == nil {
|
if bcrypt.CompareHashAndPassword([]byte(dbhsh), []byte(password)) == nil {
|
||||||
c, err := auth.CreateSession(&common.User{
|
c, err := auth.CreateSession(&common.User{
|
||||||
Username: username,
|
Username: username,
|
||||||
})
|
})
|
||||||
|
@ -244,32 +238,3 @@ func adminHandler() common.Handler {
|
||||||
return common.ReadAndServeFile("assets/web/admin.html", w)
|
return common.ReadAndServeFile("assets/web/admin.html", w)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Serve setup.html and config parameters
|
|
||||||
func serveSetup() common.Handler {
|
|
||||||
return func(rc *common.RouterContext, w http.ResponseWriter, r *http.Request) *common.HTTPError {
|
|
||||||
if r.Method == "GET" {
|
|
||||||
return common.ReadAndServeFile("assets/web/setup.html", w)
|
|
||||||
}
|
|
||||||
r.ParseMultipartForm(32 << 20)
|
|
||||||
|
|
||||||
// Parse form and convert to JSON
|
|
||||||
cnf := NewConfig{
|
|
||||||
strings.Join(r.Form["podcastname"], ""), // Podcast name
|
|
||||||
strings.Join(r.Form["podcasthost"], ""), // Podcast host
|
|
||||||
strings.Join(r.Form["podcastemail"], ""), // Podcast host email
|
|
||||||
"", // Podcast image
|
|
||||||
"", // Podcast location
|
|
||||||
"", // Podcast location
|
|
||||||
}
|
|
||||||
|
|
||||||
b, err := json.Marshal(cnf)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
ioutil.WriteFile("assets/config/config.json", b, 0644)
|
|
||||||
w.Write([]byte("Done"))
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
184
setup.go
Normal file
184
setup.go
Normal file
|
@ -0,0 +1,184 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"archive/zip"
|
||||||
|
"context"
|
||||||
|
"crypto/rand"
|
||||||
|
"database/sql"
|
||||||
|
"encoding/base64"
|
||||||
|
"fmt"
|
||||||
|
_ "github.com/mattn/go-sqlite3"
|
||||||
|
"golang.org/x/crypto/bcrypt"
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
|
||||||
|
"github.com/google/go-github/github"
|
||||||
|
)
|
||||||
|
|
||||||
|
func GenerateRandomBytes(n int) ([]byte, error) {
|
||||||
|
b := make([]byte, n)
|
||||||
|
_, err := rand.Read(b)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return b, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GenerateRandomString returns a URL-safe, base64 encoded
|
||||||
|
// securely generated random string.
|
||||||
|
func GenerateRandomString(s int) (string, error) {
|
||||||
|
b, err := GenerateRandomBytes(s)
|
||||||
|
return base64.URLEncoding.EncodeToString(b), err
|
||||||
|
}
|
||||||
|
|
||||||
|
func Setup() {
|
||||||
|
defer LockFile()
|
||||||
|
// Create users SQLite3 file
|
||||||
|
fmt.Println("Initializing the database")
|
||||||
|
|
||||||
|
os.MkdirAll("assets/config/", 0755)
|
||||||
|
os.Mkdir("podcasts", 0755)
|
||||||
|
os.Create("assets/config/users.db")
|
||||||
|
|
||||||
|
db, err := sql.Open("sqlite3", "assets/config/users.db")
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("Problem opening database file! %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = db.Exec("CREATE TABLE IF NOT EXISTS `users` ( `id` INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT UNIQUE, `username` TEXT UNIQUE, `hash` TEXT, `realname` TEXT, `email` TEXT, `permissions` INTEGER )")
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("Problem creating database! %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
text, err := GenerateRandomString(12)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("Error randomly generating password", err)
|
||||||
|
}
|
||||||
|
fmt.Println("Admin password: ", text)
|
||||||
|
hash, err := bcrypt.GenerateFromPassword([]byte(text), 4)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("Error generating hash", err)
|
||||||
|
}
|
||||||
|
if bcrypt.CompareHashAndPassword(hash, []byte(text)) == nil {
|
||||||
|
fmt.Println("Password hashed")
|
||||||
|
}
|
||||||
|
_, err = db.Exec("INSERT INTO users(id,username,hash,realname,email,permissions) VALUES (0,'admin','" + string(hash) + "','Administrator','admin@localhost',2)")
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("Problem creating database! %v", err)
|
||||||
|
}
|
||||||
|
defer db.Close()
|
||||||
|
|
||||||
|
// Download web assets
|
||||||
|
fmt.Println("Downloading web assets")
|
||||||
|
os.MkdirAll("assets/web/", 0755)
|
||||||
|
|
||||||
|
client := github.NewClient(nil).Repositories
|
||||||
|
|
||||||
|
ctx := context.Background()
|
||||||
|
res, _, err := client.GetLatestRelease(ctx, "gmemstr", "pogo-vue")
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("Problem getting latest pogo-vue release! %v", err)
|
||||||
|
}
|
||||||
|
for i := 0; i < len(res.Assets); i++ {
|
||||||
|
if res.Assets[i].GetName() == "webassets.zip" {
|
||||||
|
download := res.Assets[i]
|
||||||
|
fmt.Println("Release found: %v", download.GetBrowserDownloadURL())
|
||||||
|
tmpfile, err := os.Create(download.GetName())
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("Problem creating webassets file! %v", err)
|
||||||
|
}
|
||||||
|
var j io.Reader = (*os.File)(tmpfile)
|
||||||
|
defer tmpfile.Close()
|
||||||
|
|
||||||
|
j, s, err := client.DownloadReleaseAsset(ctx, "gmemstr", "pogo-vue", download.GetID())
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("Problem downloading webassets! %v", err)
|
||||||
|
}
|
||||||
|
if j == nil {
|
||||||
|
resp, err := http.Get(s)
|
||||||
|
defer resp.Body.Close()
|
||||||
|
_, err = io.Copy(tmpfile, resp.Body)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("Problem creating webassets file! %v", err)
|
||||||
|
}
|
||||||
|
fmt.Println("Download complete\nUnzipping")
|
||||||
|
err = Unzip(download.GetName(), "assets/web")
|
||||||
|
defer os.Remove(download.GetName()) // Remove zip
|
||||||
|
} else {
|
||||||
|
fmt.Println("Unexpected error, please open an issue!")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func LockFile() {
|
||||||
|
lock, err := os.Create(".lock")
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("Error: %v", err)
|
||||||
|
}
|
||||||
|
lock.Write([]byte("This file left intentionally empty"))
|
||||||
|
defer lock.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
// From https://stackoverflow.com/questions/20357223/easy-way-to-unzip-file-with-golang
|
||||||
|
func Unzip(src, dest string) error {
|
||||||
|
r, err := zip.OpenReader(src)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
if err := r.Close(); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
os.MkdirAll(dest, 0755)
|
||||||
|
|
||||||
|
// Closure to address file descriptors issue with all the deferred .Close() methods
|
||||||
|
extractAndWriteFile := func(f *zip.File) error {
|
||||||
|
rc, err := f.Open()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
if err := rc.Close(); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
path := filepath.Join(dest, f.Name)
|
||||||
|
|
||||||
|
if f.FileInfo().IsDir() {
|
||||||
|
os.MkdirAll(path, f.Mode())
|
||||||
|
} else {
|
||||||
|
os.MkdirAll(filepath.Dir(path), f.Mode())
|
||||||
|
f, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, f.Mode())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
if err := f.Close(); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
_, err = io.Copy(f, rc)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, f := range r.File {
|
||||||
|
err := extractAndWriteFile(f)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -10,12 +10,21 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"os"
|
||||||
|
|
||||||
"github.com/gmemstr/pogo/router"
|
"github.com/gmemstr/pogo/router"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Main function that defines routes
|
// Main function that defines routes
|
||||||
func main() {
|
func main() {
|
||||||
|
|
||||||
|
// Check if this is the first time Pogo has been run
|
||||||
|
// with a lockfile
|
||||||
|
if _, err := os.Stat(".lock"); os.IsNotExist(err) {
|
||||||
|
fmt.Println("This looks like your first time running Pogo, give me a second to set myself up.")
|
||||||
|
Setup()
|
||||||
|
}
|
||||||
|
|
||||||
// Start the watch() function in generate_rss.go, which
|
// Start the watch() function in generate_rss.go, which
|
||||||
// watches for file changes and regenerates the feed
|
// watches for file changes and regenerates the feed
|
||||||
go watch()
|
go watch()
|
||||||
|
|
Loading…
Reference in a new issue