Skip to content

Commit 5acbbcd

Browse files
authored
Merge pull request #387 from webkom/syncing-heh
Add initial syncing, heh
2 parents d2e30c5 + 27d5cc4 commit 5acbbcd

File tree

5 files changed

+306
-0
lines changed

5 files changed

+306
-0
lines changed

deployment/sync/.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
sync

deployment/sync/README.md

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
# Daemon to sync from LEGO to VOTE
2+
3+
This use a websocket connection from LEGO and create new VOTE users when a user registers on an event.
4+
5+
```
6+
Usage of ./sync:
7+
-csrf-token string
8+
A csrf token from VOTE. Look in a request. On the format: "RaNdom-String"
9+
-event int
10+
Event id to make users from. On the format: 123
11+
-lego-user-endpoint string
12+
Endpoint to users on LEGO. On the format: "https://lego-domain.no/api/v1/users/"
13+
-socket-url string
14+
Websocket url, full url w/token. Look in console on abakus.no. On the format: "wss://ws.abakus-domain.no/?jwt=long-jwt-here"
15+
-vote-cookie string
16+
Vote session cookie. On the format: "connect.sid=xasda"
17+
-vote-endpoint string
18+
Endpoint to VOTE generate. On the format: "https://vote.abakus-domain.no/api/user/generate"
19+
```

deployment/sync/go.mod

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
module github.com/webkom/vote/deployment/sync
2+
3+
go 1.15
4+
5+
require github.com/gorilla/websocket v1.4.2

deployment/sync/go.sum

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc=
2+
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=

deployment/sync/main.go

Lines changed: 279 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,279 @@
1+
package main
2+
3+
import (
4+
"bytes"
5+
"context"
6+
"encoding/json"
7+
"flag"
8+
"fmt"
9+
"io/ioutil"
10+
"log"
11+
"net/http"
12+
"net/url"
13+
"os"
14+
"os/signal"
15+
"time"
16+
17+
"github.com/gorilla/websocket"
18+
)
19+
20+
var eventId = flag.Int("event", 0, "Event id to make users from. On the format: 123")
21+
var wsUrl = flag.String("socket-url", "", `Websocket url, full url w/token. Look in console on abakus.no. On the format: "wss://ws.abakus-domain.no/?jwt=long-jwt-here"`)
22+
var authCookieVote = flag.String("vote-cookie", "", `Vote session cookie. On the format: "connect.sid=xasda"`)
23+
var csrfToken = flag.String("csrf-token", "", `A csrf token from VOTE. Look in a request. On the format: "RaNdom-String" `)
24+
var voteEndpoint = flag.String("vote-endpoint", "", `Endpoint to VOTE generate. On the format: "https://vote.abakus-domain.no/api/user/generate"`)
25+
var legoUserEndpont = flag.String("lego-user-endpoint", "", `Endpoint to users on LEGO. On the format: "https://lego-domain.no/api/v1/users/"`)
26+
27+
type LegoAction struct {
28+
Type string `json:"type"`
29+
}
30+
type LegoRegisterAction struct {
31+
Meta struct {
32+
EventID int `json:"eventId"`
33+
ActivationTime time.Time `json:"activationTime"`
34+
FromPool interface{} `json:"fromPool"`
35+
} `json:"meta"`
36+
Payload struct {
37+
ID int `json:"id"`
38+
User struct {
39+
ID int `json:"id"`
40+
Username string `json:"username"`
41+
FirstName string `json:"firstName"`
42+
LastName string `json:"lastName"`
43+
FullName string `json:"fullName"`
44+
Gender string `json:"gender"`
45+
ProfilePicture string `json:"profilePicture"`
46+
InternalEmailAddress string `json:"internalEmailAddress"`
47+
} `json:"user"`
48+
Pool interface{} `json:"pool"`
49+
Status string `json:"status"`
50+
} `json:"payload"`
51+
}
52+
type LegoUnregisterAction struct {
53+
Meta struct {
54+
EventID int `json:"eventId"`
55+
ActivationTime time.Time `json:"activationTime"`
56+
FromPool interface{} `json:"fromPool"`
57+
} `json:"meta"`
58+
Payload struct {
59+
ID int `json:"id"`
60+
User struct {
61+
ID int `json:"id"`
62+
Username string `json:"username"`
63+
FirstName string `json:"firstName"`
64+
LastName string `json:"lastName"`
65+
FullName string `json:"fullName"`
66+
Gender string `json:"gender"`
67+
ProfilePicture string `json:"profilePicture"`
68+
InternalEmailAddress string `json:"internalEmailAddress"`
69+
} `json:"user"`
70+
Pool interface{} `json:"pool"`
71+
Status string `json:"status"`
72+
} `json:"payload"`
73+
}
74+
75+
func main() {
76+
flag.Parse()
77+
if len(os.Args) == 1 {
78+
flag.Usage()
79+
os.Exit(2)
80+
}
81+
82+
if len(*wsUrl) == 0 {
83+
log.Fatal("Missing wsUrl")
84+
}
85+
if *eventId == 0 {
86+
log.Fatal("Missing eventId")
87+
}
88+
if len(*authCookieVote) == 0 {
89+
log.Fatal("Missing authCookieVote")
90+
}
91+
if len(*csrfToken) == 0 {
92+
log.Fatal("Missing csrfToken")
93+
}
94+
if len(*voteEndpoint) == 0 {
95+
log.Fatal("Missing authCookieVote")
96+
}
97+
if len(*legoUserEndpont) == 0 {
98+
log.Fatal("Missing legoUserEndpoint")
99+
}
100+
101+
log.SetFlags(0)
102+
103+
interrupt := make(chan os.Signal, 1)
104+
signal.Notify(interrupt, os.Interrupt)
105+
106+
u, _ := url.Parse(*wsUrl)
107+
jwt := u.Query()["jwt"][0]
108+
109+
for {
110+
err := runLegoToVoteSync(interrupt, u, jwt)
111+
112+
// Exit if runLegoToVoteSync returns nil, otherwise reconnect
113+
if err == nil {
114+
break
115+
}
116+
log.Printf("connection error, will reconnect: %e\n", err)
117+
time.Sleep(2 * time.Second)
118+
}
119+
120+
}
121+
func runLegoToVoteSync(interrupt chan os.Signal, url *url.URL, jwt string) error {
122+
log.Printf("connecting to %s", url.String())
123+
124+
c, _, err := websocket.DefaultDialer.Dial(url.String(), nil)
125+
if err != nil {
126+
return fmt.Errorf("dial: %e", err)
127+
}
128+
defer c.Close()
129+
130+
done := make(chan struct{})
131+
132+
go func() {
133+
defer close(done)
134+
for {
135+
_, message, err := c.ReadMessage()
136+
if err != nil {
137+
log.Println("read:", err)
138+
return
139+
}
140+
var abstractAction LegoAction
141+
err = json.Unmarshal(message, &abstractAction)
142+
if err != nil {
143+
log.Printf("Error decoding msg: %q with err: %e\n", message, err)
144+
continue
145+
}
146+
switch abstractAction.Type {
147+
case "Event.SOCKET_REGISTRATION.SUCCESS":
148+
var action LegoRegisterAction
149+
err = json.Unmarshal(message, &action)
150+
151+
if err != nil {
152+
log.Printf("Error when decoding action: %e\n", err)
153+
break
154+
}
155+
156+
if action.Meta.EventID != *eventId {
157+
break
158+
}
159+
160+
log.Printf("Registering user %q in VOTE\n", action.Payload.User.FullName)
161+
client := &http.Client{}
162+
163+
ctx, cnl := context.WithTimeout(context.Background(), 30*time.Second)
164+
defer cnl()
165+
166+
legoGet, err := http.NewRequestWithContext(ctx, "GET", *legoUserEndpont+action.Payload.User.Username, nil)
167+
if err != nil {
168+
log.Printf("Error when creating lego user req: %e\n", err)
169+
break
170+
}
171+
legoGet.Header.Add("Authorization", "JWT "+jwt)
172+
173+
resp, err := client.Do(legoGet)
174+
if err != nil {
175+
log.Printf("Error when fetching lego user: %e\n", err)
176+
break
177+
}
178+
var legoUserData struct {
179+
Email string `json:"email"`
180+
}
181+
defer resp.Body.Close()
182+
respData, err := ioutil.ReadAll(resp.Body)
183+
if err != nil {
184+
log.Printf("Error when reading lego user: %e\n", err)
185+
break
186+
}
187+
188+
err = json.Unmarshal(respData, &legoUserData)
189+
190+
if err != nil {
191+
log.Printf("Error when unmarshalling user: %e\n", err)
192+
break
193+
}
194+
195+
voteFormData := struct {
196+
Email string `json:"email"`
197+
LegoUser string `json:"legoUser"`
198+
}{
199+
Email: legoUserData.Email,
200+
LegoUser: action.Payload.User.Username,
201+
}
202+
203+
out, err := json.Marshal(voteFormData)
204+
if err != nil {
205+
log.Printf("Error when marshalling user: %e\n", err)
206+
break
207+
}
208+
209+
log.Printf("Posting to VOTE: %q", string(out))
210+
req, err := http.NewRequest("POST", *voteEndpoint, bytes.NewBuffer(out))
211+
if err != nil {
212+
log.Printf("Error when creating VOTE req: %e\n", err)
213+
break
214+
}
215+
req.Header.Add("CSRF-Token", *csrfToken)
216+
req.Header.Add("content-type", "application/json;charset=UTF-8")
217+
req.Header.Set("Cookie", *authCookieVote)
218+
219+
resp, err = client.Do(req)
220+
if err != nil {
221+
log.Printf("Error when fetching VOTE: %e\n", err)
222+
break
223+
}
224+
225+
defer resp.Body.Close()
226+
respData, err = ioutil.ReadAll(resp.Body)
227+
if err != nil {
228+
log.Printf("Error when reading from vote: %e\n", err)
229+
break
230+
}
231+
log.Printf("Creating user returned: %s, %s\n", resp.Status, respData)
232+
233+
case "Event.SOCKET_UNREGISTRATION.SUCCESS":
234+
var action LegoUnregisterAction
235+
_ = json.Unmarshal(message, &action)
236+
237+
if action.Meta.EventID != *eventId {
238+
break
239+
}
240+
241+
log.Printf("User %q unregistered from event %d\n", action.Payload.User.FullName, *eventId)
242+
243+
// We don't care?
244+
245+
}
246+
}
247+
}()
248+
249+
ticker := time.NewTicker(time.Second)
250+
defer ticker.Stop()
251+
252+
for {
253+
select {
254+
case <-done:
255+
return fmt.Errorf("unexpected disconnect")
256+
case t := <-ticker.C:
257+
// Keepalive by chatting a bit I guess
258+
err := c.WriteMessage(websocket.TextMessage, []byte(t.String()))
259+
if err != nil {
260+
log.Println("write:", err)
261+
return err
262+
}
263+
case <-interrupt:
264+
log.Println("interrupt")
265+
266+
err := c.WriteMessage(websocket.CloseMessage, websocket.FormatCloseMessage(websocket.CloseNormalClosure, ""))
267+
if err != nil {
268+
log.Println("write close:", err)
269+
return nil
270+
}
271+
select {
272+
case <-done:
273+
case <-time.After(time.Second):
274+
}
275+
return nil
276+
}
277+
}
278+
279+
}

0 commit comments

Comments
 (0)