Started work on Admin interface and websockets implementation

This commit is contained in:
Andreas Mieke 2017-01-19 02:00:09 +01:00
parent 11c27edc23
commit fc14acd76a
9 changed files with 181 additions and 123 deletions

View file

@ -1,15 +1,12 @@
package main
import (
"io"
"os"
"os/signal"
"syscall"
"golang.org/x/net/websocket"
"github.com/gin-gonic/gin"
"github.com/robfig/cron"
"gopkg.in/gin-gonic/gin.v1"
"git.1750studios.com/AniNite/SocialDragon/config"
"git.1750studios.com/AniNite/SocialDragon/database"
@ -25,13 +22,15 @@ func main() {
c := cron.New()
c.AddFunc("@every 30s", snapchat.LoadNewSnaps)
c.AddFunc("@every 30s", instagram.LoadNewInstas)
c.AddFunc("@every 5s", sendNewPicture)
c.Start()
go twitter.LoadNewTweets()
go setupGin()
ch := make(chan os.Signal)
signal.Notify(ch, syscall.SIGINT, syscall.SIGTERM)
go setupGin()
<-ch
twitter.Stop()
@ -39,23 +38,17 @@ func main() {
func setupGin() {
router := gin.Default()
router.GET("/home", renderHomepage)
router.GET("/ws", websocketEndpoint)
router.LoadHTMLGlob("templates/*")
router.Static("/static", "assets")
router.Run("127.0.0.1:8000")
}
router.GET("/", renderHomepage)
func renderHomepage(ctx *gin.Context) {
ctx.HTML(200, "index.html", gin.H{"title": "Main website"})
}
router.GET("/admin", func(c *gin.Context) { c.Redirect(301, "/admin/inbox") })
router.GET("/admin/inbox", renderAdminInbox)
router.GET("/admin/approved", renderAdminApproved)
router.GET("/admin/rejected", renderAdminRejected)
func websocketEndpoint(ctx *gin.Context) {
handler := websocket.Handler(echoHandler)
handler.ServeHTTP(ctx.Writer, ctx.Request)
}
router.GET("/ws", func(c *gin.Context) { wsHandler(c.Writer, c.Request) })
func echoHandler(ws *websocket.Conn) {
websocket.Message.Send(ws, "Test Message from Server")
io.Copy(ws, ws)
router.LoadHTMLGlob(config.C.TemplatesDirectory + "/*")
router.Static("/static", config.C.AssetsDirectory)
router.Static(config.C.ContentWebDirectory, config.C.ContentDirectory)
router.Run(config.C.BindAddress)
}

54
socialdragon/socket.go Normal file
View file

@ -0,0 +1,54 @@
package main
import (
"log"
"net/http"
"git.1750studios.com/AniNite/SocialDragon/database"
"github.com/gorilla/websocket"
)
var wsupgrader = websocket.Upgrader{
ReadBufferSize: 1024,
WriteBufferSize: 1024,
CheckOrigin: func(r *http.Request) bool {
return true
},
}
var sockets []*websocket.Conn
var lastID uint
func wsHandler(w http.ResponseWriter, r *http.Request) {
conn, err := wsupgrader.Upgrade(w, r, nil)
if err != nil {
log.Printf("Failed to set websocket upgrade: %+v", err)
return
}
pos := len(sockets)
sockets = append(sockets, conn)
for {
_, _, err := conn.ReadMessage()
if err != nil {
break
}
}
sockets = append(sockets[:pos], sockets[pos+1:]...)
}
func sendNewPicture() {
var ITs []database.Item
database.Db.Offset(lastID).Limit(1).Find(&ITs)
for i, socket := range sockets {
for _, IT := range ITs {
err := socket.WriteJSON(IT)
if IT.ID > lastID {
lastID = IT.ID
}
if err != nil {
sockets = append(sockets[:i], sockets[i+1:]...)
continue
}
}
}
}

View file

@ -0,0 +1,46 @@
{{ template "header.html" . }}
<div class="row">
<div class="large-12 columns">
<h1>{{ .title }}</h1>
<div id="images" class="row small-up-2 medium-up-3 large-up-4">
{{ range .its }}
<div class="column column-block">
{{ if .IsVideo }}
<a class="thumbnail" data-toggle="{{ .ID }}-modal">
<strong>Video</strong>
</a>
{{ else }}
<a class="thumbnail" data-toggle="{{ .ID }}-modal">
<img alt="" src="{{ .Path }}" />
</a>
{{ end }}
</div>
<div id="{{ .ID }}-modal" class="full reveal" data-reveal>
<div class="row align-top">
<div class="small-10 columns">
{{ if .IsVideo }}
<video controls>
<source src="{{ .Path }}" />
</video>
{{ else }}
<img alt="" src="{{ .Path }}" />
{{ end }}
</div>
<div class="small-2 columns">
<a class="button success" href="javascript:approveMedia({{ .ID }})">Approve</a><br />
<a class="button alert" href="javascript:rejectMedia({{ .ID }})">Reject</a>
</div>
</div>
<button class="close-button" data-close aria-label="Close reveal" type="button">
<span aria-hidden="true">&times;</span>
</button>
</div>
{{ end }}
</div>
</div>
</div>
<script type="text/javascript">
var site = "{{ .site }}";
var obj = document.getElementById("images");
</script>
{{ template "footer.html" . }}

View file

@ -1,65 +0,0 @@
<!doctype html>
<html class="no-js" lang="en" dir="ltr">
<head>
<meta charset="utf-8">
<meta http-equiv="x-ua-compatible" content="ie=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Snapchat Wall Admin</title>
<meta http-equiv="refresh" content="30">
<link rel="stylesheet" href="/static/css/foundation.css">
<link rel="stylesheet" href="/static/css/app.css">
<script>
function httpGetAsync(theUrl, callback) {
var xmlHttp = new XMLHttpRequest();
xmlHttp.onreadystatechange = function() {
if (xmlHttp.readyState == 4 && xmlHttp.status == 200)
callback(xmlHttp.responseText);
}
xmlHttp.open("GET", theUrl, true); // true for asynchronous
xmlHttp.send(null);
}
function approveSnap(myself) {
function cb(success) {
if (success == "True") {
myself.parentNode.removeChild(myself);
} else {
alert("Failed!");
}
}
url = myself.src;
filename = url.substring(url.lastIndexOf('/')+1);
if (confirm("Approve this snap?")) {
httpGetAsync("/admin/snap/approve?filename=" + filename, cb);
} else {
httpGetAsync("/admin/snap/reject?filename=" + filename, cb);
}
}
</script>
</head>
<body>
<div class="row">
<div class="large-12 columns">
<h1>Snapchat Wall &middot; Admin</h1>
<ul class="menu">
<li class="active"><a>Inbox</a></li>
<li><a>Approved</a></li>
<li><a>Rejected</a></li>
</ul>
</div>
</div>
<div class="row">
<div class="large-12 columns">
<div class="callout">
<p>Click on snap to review:</p>
<img style="width: 30%; min-width: 15em;" onclick="approveSnap(this)" class="thumbnail" src="/snaps/inbox/IMAGE_HERE" alt="" />
</div>
</div>
</div>
<script src="/static/js/vendor/jquery.js"></script>
<script src="/static/js/vendor/what-input.js"></script>
<script src="/static/js/vendor/foundation.js"></script>
<script src="/static/js/app.js"></script>
</body>
</html>

View file

@ -0,0 +1,6 @@
<script src="/static/js/vendor/jquery.js"></script>
<script src="/static/js/vendor/what-input.js"></script>
<script src="/static/js/vendor/foundation.js"></script>
<script src="/static/js/app.js"></script>
</body>
</html>

View file

@ -0,0 +1,21 @@
<!doctype html>
<html class="no-js" lang="en" dir="ltr">
<head>
<meta charset="utf-8">
<meta http-equiv="x-ua-compatible" content="ie=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{{ .title }}</title>
<link rel="stylesheet" href="/static/css/foundation.css">
<link rel="stylesheet" href="/static/css/app.css">
</head>
<body>
<div class="top-bar">
<div class="top-bar-left">
<ul class="dropdown menu" data-dropdown-menu>
<li class="menu-text">SocialDragon Admin</li>
<li><a href="/admin/inbox">Inbox</a></li>
<li><a href="/admin/approved">Approved</a></li>
<li><a href="/admin/rejected">Rejected</a></li>
</ul>
</div>
</div>

View file

@ -2,7 +2,7 @@
<html>
<head>
<meta charset="utf-8">
<title>Snapchat Wall</title>
<title>{{.title}}</title>
<style type="text/css">
@import 'https://fonts.googleapis.com/css?family=Roboto';
html, body {
@ -60,7 +60,7 @@
<div id="left">
<div id="image">
<center>
<img style="max-height: 100%; background: green; display: inline-block; vertical-align: middle;" id="snapwall_image" src="/snaps/approved/IMAGE_HERE" alt="" />
<img style="max-height: 100%; background: green; display: inline-block; vertical-align: middle;" id="snapwall_image" src="" alt="" />
</center>
</div>
</div><!--
@ -78,14 +78,15 @@
<div style="clear:both"></div>
<script type="text/javascript">
var exampleSocket = new WebSocket("ws://127.0.0.1:8000/ws");
var exampleSocket = new WebSocket("ws://192.168.2.108:8000/ws");
exampleSocket.onopen = function (event) {
console.log("WS: Connection open!");
console.log("Proto: " + exampleSocket.protocol);
exampleSocket.send("Here's some text that the server is urgently awaiting!");
};
exampleSocket.onmessage = function (event) {
console.log("WS Message:");
IT = JSON.parse(event.data);
document.getElementById("snapwall_image").src = IT["Path"];
console.log(event.data);
}
</script>

View file

@ -1,33 +0,0 @@
DatabaseConnection = ""
BindAddress = ""
AssetsDirectory = ""
TemplatesDirectory = ""
ContentDirectory = ""
ContentWebDirectory = ""
[Snapchat]
ApiBase = ""
UserAgent = ""
UserName = ""
[Snapchat.GetConversations]
Uuid = ""
ClientAuthToken = ""
RequestToken = ""
Timestamp = ""
[Snapchat.GetBlob]
Uuid = ""
ClientAuthToken = ""
RequestToken = ""
Timestamp = ""
[Snapchat.MarkAsSeen]
Uuid = ""
ClientAuthToken = ""
RequestToken = ""
Timestamp = ""
[Twitter]
ConsumerKey = ""
ConsumerSecret = ""
OAuthToken = ""
OAuthTokenSecret = ""
Filter = ""

35
socialdragon/webapp.go Normal file
View file

@ -0,0 +1,35 @@
package main
import (
"log"
"git.1750studios.com/AniNite/SocialDragon/database"
"github.com/gin-gonic/gin"
)
// Wall
func renderHomepage(c *gin.Context) {
c.HTML(200, "index.html", gin.H{"title": "SocialDragon"})
}
// Admin
func renderAdminInbox(c *gin.Context) {
var ITs []database.Item
database.Db.Order("created_at", false).Find(&ITs, "state = ?", database.Inbox)
log.Printf("%+v", ITs)
c.HTML(200, "admin-inbox.html", gin.H{"its": ITs, "title": "SocialDragon Admin Inbox", "site": "admin-inbox"})
}
func renderAdminApproved(c *gin.Context) {
var ITs []database.Item
database.Db.Order("updated_at", true).Find(&ITs, "state = ?", database.Approved)
log.Printf("%+v", ITs)
c.HTML(200, "admin-approved.html", gin.H{"its": ITs, "title": "SocialDragon Admin Approved", "site": "admin-approved"})
}
func renderAdminRejected(c *gin.Context) {
var ITs []database.Item
database.Db.Order("updated_at", true).Find(&ITs, "state = ?", database.Rejected)
log.Printf("%+v", ITs)
c.HTML(200, "admin-rejected.html", gin.H{"its": ITs, "title": "SocialDragon Admin Rejected", "site": "admin-rejected"})
}