auth
This commit is contained in:
parent
922b60f76a
commit
133b7749d4
@ -9,7 +9,7 @@ import (
|
|||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
)
|
)
|
||||||
|
|
||||||
func Listen(address string) {
|
func Listen(address string, staticPath string) {
|
||||||
router := gin.Default()
|
router := gin.Default()
|
||||||
|
|
||||||
router.GET("/info", read.Info)
|
router.GET("/info", read.Info)
|
||||||
@ -17,10 +17,16 @@ func Listen(address string) {
|
|||||||
router.POST("/set", write.Pixel)
|
router.POST("/set", write.Pixel)
|
||||||
router.OPTIONS("/set", func(c *gin.Context) {
|
router.OPTIONS("/set", func(c *gin.Context) {
|
||||||
c.Header("Access-Control-Allow-Origin", "*")
|
c.Header("Access-Control-Allow-Origin", "*")
|
||||||
c.Header("Access-Control-Allow-Headers", "*")
|
c.Header("Vary", "Origin")
|
||||||
|
c.Header("Access-Control-Allow-Headers", "content-type")
|
||||||
c.Header("Access-Control-Allow-Methods", "*")
|
c.Header("Access-Control-Allow-Methods", "*")
|
||||||
|
c.Header("Access-Control-Allow-Credentials", "true")
|
||||||
})
|
})
|
||||||
|
|
||||||
|
if staticPath != "" {
|
||||||
|
router.Static("/static", staticPath)
|
||||||
|
}
|
||||||
|
|
||||||
server := &http.Server{
|
server := &http.Server{
|
||||||
Addr: address,
|
Addr: address,
|
||||||
Handler: router,
|
Handler: router,
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
package read
|
package read
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
|
|
||||||
"git.tek.govt.hu/dowerx/place/api/structs"
|
"git.tek.govt.hu/dowerx/place/api/structs"
|
||||||
"git.tek.govt.hu/dowerx/place/config"
|
"git.tek.govt.hu/dowerx/place/config"
|
||||||
"git.tek.govt.hu/dowerx/place/storage"
|
"git.tek.govt.hu/dowerx/place/storage"
|
||||||
@ -35,5 +37,5 @@ func Tile(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func Continuous(c *gin.Context) {
|
func Continuous(c *gin.Context) {
|
||||||
|
panic(errors.New("unimplomented"))
|
||||||
}
|
}
|
||||||
|
@ -4,13 +4,15 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
|
|
||||||
"git.tek.govt.hu/dowerx/place/api/structs"
|
"git.tek.govt.hu/dowerx/place/api/structs"
|
||||||
|
"git.tek.govt.hu/dowerx/place/auth"
|
||||||
|
"git.tek.govt.hu/dowerx/place/config"
|
||||||
"git.tek.govt.hu/dowerx/place/storage"
|
"git.tek.govt.hu/dowerx/place/storage"
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
)
|
)
|
||||||
|
|
||||||
func Pixel(c *gin.Context) {
|
var timeout int = config.GetConfig().Timeout
|
||||||
// TODO: apply timeout using cookies
|
|
||||||
|
|
||||||
|
func Pixel(c *gin.Context) {
|
||||||
var info struct {
|
var info struct {
|
||||||
structs.Color
|
structs.Color
|
||||||
structs.Coordinates
|
structs.Coordinates
|
||||||
@ -20,6 +22,17 @@ func Pixel(c *gin.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cookie, err := c.Cookie("place")
|
||||||
|
if err != nil {
|
||||||
|
cookie = auth.SetCookie(timeout)
|
||||||
|
c.SetCookie("place", cookie, timeout, "/", "", false, false)
|
||||||
|
} else {
|
||||||
|
if auth.GetCookie(cookie) {
|
||||||
|
c.Status(403)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if err := storage.SetPixel(info.X, info.Y, &info); err != nil {
|
if err := storage.SetPixel(info.X, info.Y, &info); err != nil {
|
||||||
c.AbortWithError(500, err)
|
c.AbortWithError(500, err)
|
||||||
return
|
return
|
||||||
|
51
auth/auth.go
Normal file
51
auth/auth.go
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
package auth
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"math/rand"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/redis/go-redis/v9"
|
||||||
|
)
|
||||||
|
|
||||||
|
var ctx context.Context
|
||||||
|
var rdb *redis.Client
|
||||||
|
|
||||||
|
func generateToken(length int) string {
|
||||||
|
validRunes := []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ123456789")
|
||||||
|
token := make([]rune, length)
|
||||||
|
for i := range token {
|
||||||
|
token[i] = validRunes[rand.Intn(len(validRunes))]
|
||||||
|
}
|
||||||
|
return string(token)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Connect(options *redis.Options) {
|
||||||
|
ctx = context.Background()
|
||||||
|
rdb = redis.NewClient(options)
|
||||||
|
|
||||||
|
if err := rdb.Ping(ctx).Err(); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Close() {
|
||||||
|
rdb.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
func SetCookie(timeout int) string {
|
||||||
|
cookie := generateToken(32)
|
||||||
|
err := rdb.Set(ctx, cookie, cookie, time.Duration(timeout)*time.Second).Err()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return cookie
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetCookie(cookie string) bool {
|
||||||
|
resp := rdb.Exists(ctx, cookie)
|
||||||
|
if resp.Err() != nil {
|
||||||
|
panic(resp.Err())
|
||||||
|
}
|
||||||
|
return resp.Val() == 1
|
||||||
|
}
|
@ -9,6 +9,7 @@ import (
|
|||||||
type Config struct {
|
type Config struct {
|
||||||
Address string
|
Address string
|
||||||
StoragePath string
|
StoragePath string
|
||||||
|
StaticPath string
|
||||||
TileSize int
|
TileSize int
|
||||||
CanvasSize int
|
CanvasSize int
|
||||||
SaveFrequency int
|
SaveFrequency int
|
||||||
@ -23,6 +24,7 @@ func GetConfig() Config {
|
|||||||
conf = &Config{}
|
conf = &Config{}
|
||||||
flag.StringVar(&conf.Address, "address", ":8080", "API base")
|
flag.StringVar(&conf.Address, "address", ":8080", "API base")
|
||||||
flag.StringVar(&conf.StoragePath, "storage", "/data", "image storage path")
|
flag.StringVar(&conf.StoragePath, "storage", "/data", "image storage path")
|
||||||
|
flag.StringVar(&conf.StaticPath, "static", "/static", "web static path")
|
||||||
flag.IntVar(&conf.TileSize, "tile_size", 128, "width of a tile")
|
flag.IntVar(&conf.TileSize, "tile_size", 128, "width of a tile")
|
||||||
flag.IntVar(&conf.CanvasSize, "canvas_size", 4, "width of the canvas (in tiles)")
|
flag.IntVar(&conf.CanvasSize, "canvas_size", 4, "width of the canvas (in tiles)")
|
||||||
flag.IntVar(&conf.SaveFrequency, "save", 60, "seconds between saves")
|
flag.IntVar(&conf.SaveFrequency, "save", 60, "seconds between saves")
|
||||||
|
6
docker/docker-compose.yml
Normal file
6
docker/docker-compose.yml
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
services:
|
||||||
|
redis:
|
||||||
|
image: redis:alpine
|
||||||
|
network_mode: host
|
||||||
|
environment:
|
||||||
|
- REDIS_ARGS=--requirepass redis
|
10
main.go
10
main.go
@ -2,13 +2,21 @@ package main
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"git.tek.govt.hu/dowerx/place/api"
|
"git.tek.govt.hu/dowerx/place/api"
|
||||||
|
"git.tek.govt.hu/dowerx/place/auth"
|
||||||
"git.tek.govt.hu/dowerx/place/config"
|
"git.tek.govt.hu/dowerx/place/config"
|
||||||
"git.tek.govt.hu/dowerx/place/storage"
|
"git.tek.govt.hu/dowerx/place/storage"
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
conf := config.GetConfig()
|
conf := config.GetConfig()
|
||||||
|
|
||||||
storage.Load(conf.StoragePath, conf.CanvasSize, conf.TileSize)
|
storage.Load(conf.StoragePath, conf.CanvasSize, conf.TileSize)
|
||||||
|
defer storage.Save()
|
||||||
|
|
||||||
storage.StartSaves(conf.SaveFrequency)
|
storage.StartSaves(conf.SaveFrequency)
|
||||||
api.Listen(conf.Address)
|
|
||||||
|
auth.Connect(&conf.Redis)
|
||||||
|
defer auth.Close()
|
||||||
|
|
||||||
|
api.Listen(conf.Address, conf.StaticPath)
|
||||||
}
|
}
|
||||||
|
@ -6,9 +6,8 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"image"
|
"image"
|
||||||
"image/color"
|
"image/color"
|
||||||
"image/draw"
|
|
||||||
"log"
|
"log"
|
||||||
"os"
|
"path/filepath"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -26,10 +25,6 @@ func (err *WrongSizeError) Error() string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func Load(path string, canvasSize int, tileSize int) {
|
func Load(path string, canvasSize int, tileSize int) {
|
||||||
if err := os.Chdir(path); err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
tiles = make([][]tile, canvasSize)
|
tiles = make([][]tile, canvasSize)
|
||||||
for i := range tiles {
|
for i := range tiles {
|
||||||
tiles[i] = make([]tile, canvasSize)
|
tiles[i] = make([]tile, canvasSize)
|
||||||
@ -37,31 +32,16 @@ func Load(path string, canvasSize int, tileSize int) {
|
|||||||
|
|
||||||
for y := range tiles {
|
for y := range tiles {
|
||||||
for x := range tiles[y] {
|
for x := range tiles[y] {
|
||||||
tiles[y][x].image = image.NewRGBA(image.Rect(0, 0, tileSize, tileSize))
|
err := tiles[y][x].load(filepath.Join(path, fmt.Sprintf("%d-%d.png", x, y)), tileSize)
|
||||||
filename := fmt.Sprintf("%d-%d.png", x, y)
|
|
||||||
file, err := os.Open(filename)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
tiles[y][x].fill()
|
tiles[y][x].fill()
|
||||||
if err = tiles[y][x].save(filename); err != nil {
|
if err = tiles[y][x].save(); err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Printf("Created tile (%d-%d)\n", x, y)
|
log.Printf("Created tile (%d-%d)\n", x, y)
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
img, _, err := image.Decode(file)
|
log.Printf("Loaded tile (%d-%d)\n", x, y)
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
}
|
||||||
draw.Draw(tiles[y][x].image, img.Bounds(), img, img.Bounds().Min, draw.Src)
|
|
||||||
}
|
|
||||||
defer file.Close()
|
|
||||||
|
|
||||||
if tiles[y][x].image.Bounds().Size().X != tileSize && tiles[y][x].image.Bounds().Size().Y != tileSize {
|
|
||||||
panic(WrongSizeError{ExpectedSize: tileSize, ActualSize: tiles[y][x].image.Bounds().Size(), X: x, Y: y})
|
|
||||||
}
|
|
||||||
|
|
||||||
tiles[y][x].dirty = false
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -70,9 +50,7 @@ func Save() {
|
|||||||
for y := range tiles {
|
for y := range tiles {
|
||||||
for x := range tiles[y] {
|
for x := range tiles[y] {
|
||||||
if tiles[y][x].dirty {
|
if tiles[y][x].dirty {
|
||||||
filename := fmt.Sprintf("%d-%d.png", x, y)
|
if err := tiles[y][x].save(); err != nil {
|
||||||
|
|
||||||
if err := tiles[y][x].save(filename); err != nil {
|
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -10,11 +10,32 @@ import (
|
|||||||
|
|
||||||
type tile struct {
|
type tile struct {
|
||||||
dirty bool
|
dirty bool
|
||||||
|
path string
|
||||||
image *image.RGBA
|
image *image.RGBA
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *tile) save(path string) error {
|
func (t *tile) load(path string, tileSize int) error {
|
||||||
file, err := os.OpenFile(path, os.O_WRONLY, 0644)
|
t.image = image.NewRGBA(image.Rect(0, 0, tileSize, tileSize))
|
||||||
|
t.path = path
|
||||||
|
file, err := os.Open(path)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer file.Close()
|
||||||
|
|
||||||
|
img, _, err := image.Decode(file)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
draw.Draw(t.image, img.Bounds(), img, img.Bounds().Min, draw.Src)
|
||||||
|
t.dirty = false
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *tile) save() error {
|
||||||
|
file, err := os.OpenFile(t.path, os.O_WRONLY|os.O_CREATE, 0644)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
18
web/place.js
18
web/place.js
@ -17,9 +17,9 @@ async function setPixel(canvas, ctx, x, y, c) {
|
|||||||
x = x - bounds.left;
|
x = x - bounds.left;
|
||||||
y = y - bounds.top;
|
y = y - bounds.top;
|
||||||
|
|
||||||
try {
|
let response = await fetch(`${apiBase}/set`, {
|
||||||
await fetch(`${apiBase}/set`, {
|
|
||||||
method: "POST",
|
method: "POST",
|
||||||
|
credentials: 'include',
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json'
|
'Content-Type': 'application/json'
|
||||||
},
|
},
|
||||||
@ -32,10 +32,22 @@ async function setPixel(canvas, ctx, x, y, c) {
|
|||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
|
console.log(response);
|
||||||
|
|
||||||
|
switch (response.status) {
|
||||||
|
case 200:
|
||||||
ctx.fillStyle = `rgb(${c.r}, ${c.g}, ${c.b})`;
|
ctx.fillStyle = `rgb(${c.r}, ${c.g}, ${c.b})`;
|
||||||
ctx.fillRect(x, y, 1, 1);
|
ctx.fillRect(x, y, 1, 1);
|
||||||
} catch {
|
break;
|
||||||
|
case 400:
|
||||||
|
alert("missing parameters in request");
|
||||||
|
break;
|
||||||
|
case 403:
|
||||||
|
alert("not allowed to set another pixel yet");
|
||||||
|
break;
|
||||||
|
case 500:
|
||||||
alert(`failed to set pixel at ${x}-${y} to ${c}`);
|
alert(`failed to set pixel at ${x}-${y} to ${c}`);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user