auth
This commit is contained in:
parent
922b60f76a
commit
133b7749d4
@ -9,7 +9,7 @@ import (
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
func Listen(address string) {
|
||||
func Listen(address string, staticPath string) {
|
||||
router := gin.Default()
|
||||
|
||||
router.GET("/info", read.Info)
|
||||
@ -17,10 +17,16 @@ func Listen(address string) {
|
||||
router.POST("/set", write.Pixel)
|
||||
router.OPTIONS("/set", func(c *gin.Context) {
|
||||
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-Credentials", "true")
|
||||
})
|
||||
|
||||
if staticPath != "" {
|
||||
router.Static("/static", staticPath)
|
||||
}
|
||||
|
||||
server := &http.Server{
|
||||
Addr: address,
|
||||
Handler: router,
|
||||
|
@ -1,6 +1,8 @@
|
||||
package read
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"git.tek.govt.hu/dowerx/place/api/structs"
|
||||
"git.tek.govt.hu/dowerx/place/config"
|
||||
"git.tek.govt.hu/dowerx/place/storage"
|
||||
@ -35,5 +37,5 @@ func Tile(c *gin.Context) {
|
||||
}
|
||||
|
||||
func Continuous(c *gin.Context) {
|
||||
|
||||
panic(errors.New("unimplomented"))
|
||||
}
|
||||
|
@ -4,13 +4,15 @@ import (
|
||||
"errors"
|
||||
|
||||
"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"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
func Pixel(c *gin.Context) {
|
||||
// TODO: apply timeout using cookies
|
||||
var timeout int = config.GetConfig().Timeout
|
||||
|
||||
func Pixel(c *gin.Context) {
|
||||
var info struct {
|
||||
structs.Color
|
||||
structs.Coordinates
|
||||
@ -20,6 +22,17 @@ func Pixel(c *gin.Context) {
|
||||
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 {
|
||||
c.AbortWithError(500, err)
|
||||
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 {
|
||||
Address string
|
||||
StoragePath string
|
||||
StaticPath string
|
||||
TileSize int
|
||||
CanvasSize int
|
||||
SaveFrequency int
|
||||
@ -23,6 +24,7 @@ func GetConfig() Config {
|
||||
conf = &Config{}
|
||||
flag.StringVar(&conf.Address, "address", ":8080", "API base")
|
||||
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.CanvasSize, "canvas_size", 4, "width of the canvas (in tiles)")
|
||||
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 (
|
||||
"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/storage"
|
||||
)
|
||||
|
||||
func main() {
|
||||
conf := config.GetConfig()
|
||||
|
||||
storage.Load(conf.StoragePath, conf.CanvasSize, conf.TileSize)
|
||||
defer storage.Save()
|
||||
|
||||
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"
|
||||
"image"
|
||||
"image/color"
|
||||
"image/draw"
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"time"
|
||||
)
|
||||
|
||||
@ -26,10 +25,6 @@ func (err *WrongSizeError) Error() string {
|
||||
}
|
||||
|
||||
func Load(path string, canvasSize int, tileSize int) {
|
||||
if err := os.Chdir(path); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
tiles = make([][]tile, canvasSize)
|
||||
for i := range tiles {
|
||||
tiles[i] = make([]tile, canvasSize)
|
||||
@ -37,31 +32,16 @@ func Load(path string, canvasSize int, tileSize int) {
|
||||
|
||||
for y := range tiles {
|
||||
for x := range tiles[y] {
|
||||
tiles[y][x].image = image.NewRGBA(image.Rect(0, 0, tileSize, tileSize))
|
||||
filename := fmt.Sprintf("%d-%d.png", x, y)
|
||||
file, err := os.Open(filename)
|
||||
err := tiles[y][x].load(filepath.Join(path, fmt.Sprintf("%d-%d.png", x, y)), tileSize)
|
||||
if err != nil {
|
||||
tiles[y][x].fill()
|
||||
if err = tiles[y][x].save(filename); err != nil {
|
||||
if err = tiles[y][x].save(); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
log.Printf("Created tile (%d-%d)\n", x, y)
|
||||
|
||||
} else {
|
||||
img, _, err := image.Decode(file)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
draw.Draw(tiles[y][x].image, img.Bounds(), img, img.Bounds().Min, draw.Src)
|
||||
log.Printf("Loaded tile (%d-%d)\n", x, y)
|
||||
}
|
||||
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 x := range tiles[y] {
|
||||
if tiles[y][x].dirty {
|
||||
filename := fmt.Sprintf("%d-%d.png", x, y)
|
||||
|
||||
if err := tiles[y][x].save(filename); err != nil {
|
||||
if err := tiles[y][x].save(); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
@ -10,11 +10,32 @@ import (
|
||||
|
||||
type tile struct {
|
||||
dirty bool
|
||||
path string
|
||||
image *image.RGBA
|
||||
}
|
||||
|
||||
func (t *tile) save(path string) error {
|
||||
file, err := os.OpenFile(path, os.O_WRONLY, 0644)
|
||||
func (t *tile) load(path string, tileSize int) error {
|
||||
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 {
|
||||
return err
|
||||
}
|
||||
|
48
web/place.js
48
web/place.js
@ -17,25 +17,37 @@ async function setPixel(canvas, ctx, x, y, c) {
|
||||
x = x - bounds.left;
|
||||
y = y - bounds.top;
|
||||
|
||||
try {
|
||||
await fetch(`${apiBase}/set`, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({
|
||||
x: x,
|
||||
y: y,
|
||||
r: c.r,
|
||||
g: c.g,
|
||||
b: c.b
|
||||
})
|
||||
});
|
||||
let response = await fetch(`${apiBase}/set`, {
|
||||
method: "POST",
|
||||
credentials: 'include',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({
|
||||
x: x,
|
||||
y: y,
|
||||
r: c.r,
|
||||
g: c.g,
|
||||
b: c.b
|
||||
})
|
||||
});
|
||||
|
||||
ctx.fillStyle = `rgb(${c.r}, ${c.g}, ${c.b})`;
|
||||
ctx.fillRect(x, y, 1, 1);
|
||||
} catch {
|
||||
alert(`failed to set pixel at ${x}-${y} to ${c}`);
|
||||
console.log(response);
|
||||
|
||||
switch (response.status) {
|
||||
case 200:
|
||||
ctx.fillStyle = `rgb(${c.r}, ${c.g}, ${c.b})`;
|
||||
ctx.fillRect(x, y, 1, 1);
|
||||
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}`);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user