1
Fork 0
mirror of https://github.com/jech/galene.git synced 2024-11-22 08:35:57 +01:00

Encode group location in the status.json file.

This commit is contained in:
Juliusz Chroboczek 2022-09-01 15:38:29 +02:00
parent 4bc873a574
commit b55e531aa5
5 changed files with 67 additions and 34 deletions

View file

@ -28,31 +28,33 @@ server to client direction.
## Before connecting ## Before connecting
Before it connects and joins a group, a client may perform an HTTP GET The client needs to know the location of the group, the (user-visible) URL
request on the URL `/public-groups.json`. This yields a JSON array of at which the group is found. This may be obtained either by explicit
objects, one for each group that has been marked public in its configuration by the user, or by parsing the `/public-groups.json` file
configuration file. Each object has the following fields: which may contain an array of group statuses (see below).
A client then performs an HTTP GET request on the file `.status.json` at
the group's location. This yields a single JSON object, which contains
the following fields:
- `name`: the group's name - `name`: the group's name
- `displayName` (optional): a longer version of the name used for display; - `location`: the group's location
- `description` (optional): a user-readable description. - `endpoint`: the URL of the server's WebSocket endpoint
- `displayName`: a longer version of the name used for display;
- `description`: a user-readable description;
- `authServer`: the URL of the authentication server, if any;
- `authPortal`: the uRL of the authentication portal, if any;
- `locked`: true if the group is locked; - `locked`: true if the group is locked;
- `clientCount`: the number of clients currently in the group. - `clientCount`: the number of clients currently in the group.
If token-based authorisation is in use for the group, then the dictionary All fields are optional except `name`, `location` and `endpoint`.
contains the following additional field:
- `authServer`: the URL of the authorisation server.
A client may also fetch the URL `/group/name/.status.json` to retrieve the
status of a single group. If the group has not been marked as public,
then the fields `locked` and `clientCount` are omitted.
## Connecting ## Connecting
The client connects to the websocket at `/ws`. Galene uses a symmetric, The client connects to the websocket at the URL obtained at the previous
asynchronous protocol: there are no requests and responses, and most step. Galene uses a symmetric, asynchronous protocol: there are no
messages may be sent by either peer. requests and responses, and most messages may be sent by either peer.
## Message syntax ## Message syntax

View file

@ -1177,6 +1177,7 @@ func (desc *Description) GetPermission(group string, creds ClientCredentials) (s
type Status struct { type Status struct {
Name string `json:"name"` Name string `json:"name"`
Location string `json:"location"`
Endpoint string `json:"endpoint"` Endpoint string `json:"endpoint"`
DisplayName string `json:"displayName,omitempty"` DisplayName string `json:"displayName,omitempty"`
Description string `json:"description,omitempty"` Description string `json:"description,omitempty"`
@ -1186,10 +1187,40 @@ type Status struct {
ClientCount *int `json:"clientCount,omitempty"` ClientCount *int `json:"clientCount,omitempty"`
} }
func (g *Group) Status(authentified bool, endpoint string) Status { // Status returns a group's status.
// Base is the base URL for groups; if omitted, then both the Location and
// Endpoint members are omitted from the result.
func (g *Group) Status(authentified bool, base string) Status {
desc := g.Description() desc := g.Description()
var location, endpoint string
if base != "" {
burl, err := url.Parse(base)
if err == nil {
wss := "wss"
if burl.Scheme == "http" {
wss = "ws"
}
l := url.URL{
Scheme: burl.Scheme,
Host: burl.Host,
Path: path.Join(burl.Path, g.name) + "/",
}
location = l.String()
e := url.URL{
Scheme: wss,
Host: burl.Host,
Path: "/ws",
}
endpoint = e.String()
} else {
log.Printf("Couldn't parse base URL %v", base)
}
}
d := Status{ d := Status{
Name: g.name, Name: g.name,
Location: location,
Endpoint: endpoint, Endpoint: endpoint,
DisplayName: desc.DisplayName, DisplayName: desc.DisplayName,
AuthServer: desc.AuthServer, AuthServer: desc.AuthServer,
@ -1207,11 +1238,11 @@ func (g *Group) Status(authentified bool, endpoint string) Status {
return d return d
} }
func GetPublic() []Status { func GetPublic(base string) []Status {
gs := make([]Status, 0) gs := make([]Status, 0)
Range(func(g *Group) bool { Range(func(g *Group) bool {
if g.Description().Public { if g.Description().Public {
gs = append(gs, g.Status(false, "")) gs = append(gs, g.Status(false, base))
} }
return true return true
}) })

View file

@ -42,7 +42,7 @@ func TestGroup(t *testing.T) {
t.Errorf("Expected [], got %v", subs) t.Errorf("Expected [], got %v", subs)
} }
if public := GetPublic(); len(public) != 1 || public[0].Name != "group/subgroup" { if public := GetPublic(""); len(public) != 1 || public[0].Name != "group/subgroup" {
t.Errorf("Expected group/subgroup, got %v", public) t.Errorf("Expected group/subgroup, got %v", public)
} }
} }

View file

@ -110,7 +110,7 @@ async function listPublicGroups() {
let td = document.createElement('td'); let td = document.createElement('td');
let a = document.createElement('a'); let a = document.createElement('a');
a.textContent = group.displayName || group.name; a.textContent = group.displayName || group.name;
a.href = '/group/' + group.name + '/'; a.href = group.location;
td.appendChild(a); td.appendChild(a);
tr.appendChild(td); tr.appendChild(td);
let td2 = document.createElement('td'); let td2 = document.createElement('td');

View file

@ -332,9 +332,18 @@ func groupHandler(w http.ResponseWriter, r *http.Request) {
serveFile(w, r, filepath.Join(StaticRoot, "galene.html")) serveFile(w, r, filepath.Join(StaticRoot, "galene.html"))
} }
func groupBase(r *http.Request) string {
base := url.URL{
Scheme: r.URL.Scheme,
Host: r.Host,
Path: "/group/",
}
return base.String()
}
func groupStatusHandler(w http.ResponseWriter, r *http.Request) { func groupStatusHandler(w http.ResponseWriter, r *http.Request) {
path := path.Dir(r.URL.Path) pth := path.Dir(r.URL.Path)
name := parseGroupName("/group/", path) name := parseGroupName("/group/", pth)
if name == "" { if name == "" {
notFound(w) notFound(w)
return return
@ -351,16 +360,7 @@ func groupStatusHandler(w http.ResponseWriter, r *http.Request) {
return return
} }
scheme := "wss" d := g.Status(false, groupBase(r))
if Insecure {
scheme = "ws"
}
endpoint := url.URL{
Scheme: scheme,
Host: r.Host,
Path: "/ws",
}
d := g.Status(false, endpoint.String())
w.Header().Set("content-type", "application/json") w.Header().Set("content-type", "application/json")
w.Header().Set("cache-control", "no-cache") w.Header().Set("cache-control", "no-cache")
@ -380,7 +380,7 @@ func publicHandler(w http.ResponseWriter, r *http.Request) {
return return
} }
g := group.GetPublic() g := group.GetPublic(groupBase(r))
e := json.NewEncoder(w) e := json.NewEncoder(w)
e.Encode(g) e.Encode(g)
} }