Skip to content

Commit b43c5b6

Browse files
committed
Implemented websocket in server
1 parent 2a944f5 commit b43c5b6

File tree

13 files changed

+691
-212
lines changed

13 files changed

+691
-212
lines changed

go.mod

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,17 +5,19 @@ go 1.21
55
require (
66
github.com/Appboy/webpush-go v0.0.0-20221006204155-f206645c3cb7
77
github.com/davecgh/go-spew v1.1.1
8+
github.com/go-chi/chi v1.5.4
89
github.com/go-chi/chi/v5 v5.0.10
910
github.com/google/uuid v1.3.0
11+
github.com/gorilla/websocket v1.5.1
1012
github.com/urfave/cli/v2 v2.25.7
1113
golang.org/x/exp v0.0.0-20230811145659-89c5cff77bcb
1214
)
1315

1416
require (
1517
github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect
16-
github.com/go-chi/chi v1.5.4 // indirect
1718
github.com/golang-jwt/jwt v3.2.2+incompatible // indirect
1819
github.com/russross/blackfriday/v2 v2.1.0 // indirect
1920
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect
20-
golang.org/x/crypto v0.0.0-20190131182504-b8fe1690c613 // indirect
21+
golang.org/x/crypto v0.14.0 // indirect
22+
golang.org/x/net v0.17.0 // indirect
2123
)

go.sum

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,18 @@ github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keL
1212
github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I=
1313
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
1414
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
15+
github.com/gorilla/websocket v1.5.1 h1:gmztn0JnHVt9JZquRuzLw3g4wouNVzKL15iLr/zn/QY=
16+
github.com/gorilla/websocket v1.5.1/go.mod h1:x3kM2JMyaluk02fnUJpQuwD2dCS5NDG2ZHL0uE0tcaY=
1517
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
1618
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
1719
github.com/urfave/cli/v2 v2.25.7 h1:VAzn5oq403l5pHjc4OhD54+XGO9cdKVL/7lDjF+iKUs=
1820
github.com/urfave/cli/v2 v2.25.7/go.mod h1:8qnjx1vcq5s2/wpsqoZFndg2CE5tNFyrTvS6SinrnYQ=
1921
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 h1:bAn7/zixMGCfxrRTfdpNzjtPYqr8smhKouy9mxVdGPU=
2022
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673/go.mod h1:N3UwUGtsrSj3ccvlPHLoLsHnpR27oXr4ZE984MbSER8=
21-
golang.org/x/crypto v0.0.0-20190131182504-b8fe1690c613 h1:MQ/ZZiDsUapFFiMS+vzwXkCTeEKaum+Do5rINYJDmxc=
2223
golang.org/x/crypto v0.0.0-20190131182504-b8fe1690c613/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
24+
golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc=
25+
golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4=
2326
golang.org/x/exp v0.0.0-20230811145659-89c5cff77bcb h1:mIKbk8weKhSeLH2GmUTrvx8CjkyJmnU1wFmg59CUjFA=
2427
golang.org/x/exp v0.0.0-20230811145659-89c5cff77bcb/go.mod h1:FXUEEKJgO7OQYeo8N01OfiKP8RXMtf6e8aTskBGqWdc=
28+
golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM=
29+
golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE=

internal/feed/feed.go

Lines changed: 68 additions & 140 deletions
Original file line numberDiff line numberDiff line change
@@ -3,17 +3,14 @@ package feed
33
import (
44
"fmt"
55
"io"
6-
"io/fs"
76
"net/http"
87
"net/url"
98
"os"
109
"path"
1110
"path/filepath"
12-
"sort"
1311
"strings"
1412
"time"
1513

16-
"github.com/google/uuid"
1714
"golang.org/x/exp/slog"
1815
)
1916

@@ -46,8 +43,9 @@ type PublicFeed struct {
4643
// Feed is the internal representation of a Feed and contains all the
4744
// informations needed to perform its tasks
4845
type Feed struct {
49-
Path string
50-
Config FeedConfig
46+
Path string
47+
Config FeedConfig
48+
WebSocketManager *WebSocketManager
5149
}
5250

5351
type PublicFeedItem struct {
@@ -57,141 +55,42 @@ type PublicFeedItem struct {
5755
Feed *PublicFeed `json:"-"`
5856
}
5957

60-
func NewFeed(basePath string, feedName string) (*Feed, error) {
61-
slog.Info("Creating new feed", slog.String("feed", feedName))
62-
feedPath := path.Join(basePath, feedName)
63-
64-
_, err := os.Stat(feedPath)
65-
if err == nil {
66-
return nil, &FeedError{
67-
Code: 400,
68-
Message: "Feed already exists",
69-
}
70-
}
71-
72-
err = os.Mkdir(feedPath, 0700)
73-
if err != nil {
74-
slog.Error("Error creating feed directory", slog.String("feed", feedName), slog.String("directory", feedPath))
75-
return nil, err
76-
}
77-
78-
feed := Feed{
79-
Path: feedPath,
80-
Config: FeedConfig{
81-
Secret: uuid.NewString(),
82-
},
83-
}
84-
85-
feed.Config.feed = &feed
86-
87-
err = feed.Config.Write()
88-
89-
if err != nil {
90-
slog.Error("Unable to write config %s", err.Error())
91-
return nil, err
92-
}
93-
94-
return &feed, nil
58+
func (feed *Feed) Name() string {
59+
return path.Base(feed.Path)
9560
}
96-
func GetFeed(feedPath string) (*Feed, error) {
97-
if _, err := os.Stat(feedPath); os.IsNotExist(err) {
98-
return nil, &FeedError{
99-
Code: 404,
100-
Message: "Feed does not exists",
101-
}
102-
}
61+
func (feed *Feed) GetPublicItem(i string) (*PublicFeedItem, error) {
62+
feedLog := slog.Default().With(slog.String("feed", feed.Name()))
10363

104-
result := &Feed{Path: feedPath}
64+
s, err := os.Stat(path.Join(feed.Path, i))
10565

106-
c, err := FeedConfigForFeed(result)
10766
if err != nil {
108-
return nil, &FeedError{
109-
Code: 404,
110-
Message: "Feed does not exists",
111-
}
112-
}
113-
c.feed = result
114-
115-
result.Config = *c
116-
117-
return result, nil
118-
}
119-
120-
func GetPublicFeed(basePath string, feedName string, secret string) (*PublicFeed, error) {
121-
feedPath := path.Join(basePath, feedName)
122-
123-
feedLog := slog.Default().With(slog.String("feed", feedName))
124-
125-
feedLog.Debug("Getting feed", slog.Int("secret_len", len(secret)))
126-
127-
f, err := GetFeed(feedPath)
67+
code := 500
68+
e := "Unable to read file info"
12869

129-
err = f.IsSecretValid(secret)
70+
feedLog.Error(e, slog.Int("return", code))
13071

131-
if err != nil {
13272
return nil, err
13373
}
13474

135-
publicFeed := &PublicFeed{
136-
Name: feedName,
137-
Secret: f.Config.Secret,
138-
}
139-
var d []fs.DirEntry
140-
if d, err = os.ReadDir(feedPath); err != nil {
141-
code := 500
142-
feedLog.Error("Unable to feed content", slog.Int("return", code))
143-
144-
return nil, &FeedError{
145-
Code: code,
146-
Message: "Unable to open directory for read",
147-
}
148-
}
149-
150-
items := []PublicFeedItem{}
151-
for _, f := range d {
152-
if f.Name() == "secret" || f.Name() == "pin" || f.Name() == "config.json" {
153-
continue
154-
}
155-
info, err := f.Info()
156-
if err != nil {
157-
code := 500
158-
e := "Unable to read file info"
159-
160-
feedLog.Error(e, slog.Int("return", code))
161-
162-
return nil, &FeedError{
163-
Code: code,
164-
Message: e,
165-
}
166-
}
167-
168-
var itemType FeedItemType
169-
if strings.HasSuffix(f.Name(), ".txt") {
170-
itemType = Text
171-
} else if strings.HasSuffix(f.Name(), ".png") || strings.HasSuffix(f.Name(), ".jpg") {
172-
itemType = Image
173-
} else {
174-
itemType = Binary
175-
}
176-
items = append(items, PublicFeedItem{
177-
Name: f.Name(),
178-
Date: info.ModTime(),
179-
Type: itemType,
180-
Feed: publicFeed,
181-
})
75+
var itemType FeedItemType
76+
if strings.HasSuffix(i, ".txt") {
77+
itemType = Text
78+
} else if strings.HasSuffix(i, ".png") || strings.HasSuffix(i, ".jpg") {
79+
itemType = Image
80+
} else {
81+
itemType = Binary
18282
}
183-
sort.Slice(items, func(i, j2 int) bool {
184-
return items[i].Date.After(items[j2].Date)
185-
})
186-
187-
publicFeed.Items = items
188-
189-
return publicFeed, nil
190-
}
191-
func (feed *Feed) Name() string {
192-
return path.Base(feed.Path)
83+
return &PublicFeedItem{
84+
Name: i,
85+
Date: s.ModTime(),
86+
Type: itemType,
87+
Feed: &PublicFeed{
88+
Name: feed.Name(),
89+
Secret: feed.Config.Secret,
90+
},
91+
}, nil
19392
}
194-
func (feed *Feed) GetItem(item string) ([]byte, error) {
93+
func (feed *Feed) GetItemData(item string) ([]byte, error) {
19594
// Read item content
19695
slog.Info("Getting Item", slog.String("feed", feed.Name()), slog.String("name", item))
19796
var content []byte
@@ -241,8 +140,8 @@ func (feed *Feed) IsSecretValid(secret string) error {
241140
return nil
242141
}
243142

244-
func (feed *Feed) AddItem(contentType string, f io.ReadCloser) error {
245-
slog.Debug("Adding Item", slog.String("feed", feed.Name()), slog.String("content-type", contentType))
143+
func (f *Feed) AddItem(contentType string, r io.ReadCloser) error {
144+
slog.Debug("Adding Item", slog.String("feed", f.Name()), slog.String("content-type", contentType))
246145
fileExtensions := map[string]string{
247146
"image/png": "png",
248147
"image/jpeg": "jpg",
@@ -265,7 +164,7 @@ func (feed *Feed) AddItem(contentType string, f io.ReadCloser) error {
265164
}
266165
}
267166

268-
content, err := io.ReadAll(f)
167+
content, err := io.ReadAll(r)
269168
if err != nil {
270169
_, ok := err.(*http.MaxBytesError)
271170
if ok {
@@ -291,7 +190,7 @@ func (feed *Feed) AddItem(contentType string, f io.ReadCloser) error {
291190
var filename string
292191
for {
293192
filename = fmt.Sprintf("%s %d", template, fileIndex)
294-
matches, err := filepath.Glob(path.Join(feed.Path, filename) + ".*")
193+
matches, err := filepath.Glob(path.Join(f.Path, filename) + ".*")
295194
if err != nil {
296195
return &FeedError{
297196
Code: 500,
@@ -304,24 +203,45 @@ func (feed *Feed) AddItem(contentType string, f io.ReadCloser) error {
304203
fileIndex++
305204
}
306205

307-
err = os.WriteFile(path.Join(feed.Path, filename+"."+ext), content, 0600)
206+
err = os.WriteFile(path.Join(f.Path, filename+"."+ext), content, 0600)
308207
if err != nil {
309208
return &FeedError{
310209
Code: 500,
311210
Message: "Unable to write file",
312211
}
313212
}
314213

315-
slog.Info("Added Item", slog.String("name", filename+"."+ext), slog.String("feed", feed.Path), slog.String("content-type", contentType))
214+
publicItem, err := f.GetPublicItem(filename + "." + ext)
215+
216+
if err != nil {
217+
return err.(*FeedError)
218+
}
219+
220+
if err = f.WebSocketManager.NotifyAdd(publicItem); err != nil {
221+
return &FeedError{
222+
Code: 500,
223+
Message: err.Error(),
224+
}
225+
}
226+
227+
slog.Info("Added Item", slog.String("name", filename+"."+ext), slog.String("feed", f.Path), slog.String("content-type", contentType))
316228

317229
return nil
318230
}
319231

320-
func (feed *Feed) RemoveItem(item string) error {
321-
slog.Debug("Remove Item", slog.String("name", item), slog.String("feed", feed.Path))
322-
itemPath := path.Join(feed.Path, item)
232+
func (f *Feed) RemoveItem(item string) error {
233+
slog.Debug("Remove Item", slog.String("name", item), slog.String("feed", f.Path))
234+
itemPath := path.Join(f.Path, item)
323235

324-
err := os.Remove(itemPath)
236+
publicItem, err := f.GetPublicItem(item)
237+
if err != nil {
238+
return &FeedError{
239+
Code: 500,
240+
Message: err.Error(),
241+
}
242+
}
243+
244+
err = os.Remove(itemPath)
325245
if err != nil {
326246
if os.IsNotExist(err) {
327247
return &FeedError{
@@ -334,7 +254,15 @@ func (feed *Feed) RemoveItem(item string) error {
334254
Message: err.Error(),
335255
}
336256
}
337-
slog.Info("Removed Item", slog.String("name", item), slog.String("feed", feed.Name()))
257+
258+
if err = f.WebSocketManager.NotifyRemove(publicItem); err != nil {
259+
return &FeedError{
260+
Code: 500,
261+
Message: err.Error(),
262+
}
263+
}
264+
265+
slog.Info("Removed Item", slog.String("name", item), slog.String("feed", f.Name()))
338266
return nil
339267
}
340268

internal/feed/feedConfig.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,10 @@ func FeedConfigForFeed(f *Feed) (*FeedConfig, error) {
6666

6767
configPath := path.Join(f.Path, "config.json")
6868
if _, err := os.Stat(configPath); errors.Is(err, os.ErrNotExist) {
69-
result.migratev1v2()
69+
if err := result.migratev1v2(); err != nil {
70+
return nil, err
71+
}
72+
7073
}
7174
b, err := os.ReadFile(configPath)
7275
if err != nil {

0 commit comments

Comments
 (0)