[Chat] Add /c/:code handler to bskyweb (#10630)
This commit is contained in:
@@ -46,6 +46,12 @@ func run(args []string) {
|
|||||||
Required: false,
|
Required: false,
|
||||||
EnvVars: []string{"OGCARD_HOST"},
|
EnvVars: []string{"OGCARD_HOST"},
|
||||||
},
|
},
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: "chat-host",
|
||||||
|
Usage: "scheme, hostname, and port of chat appview",
|
||||||
|
Required: false,
|
||||||
|
EnvVars: []string{"CHAT_HOST"},
|
||||||
|
},
|
||||||
&cli.StringFlag{
|
&cli.StringFlag{
|
||||||
Name: "http-address",
|
Name: "http-address",
|
||||||
Usage: "Specify the local IP/port to bind to",
|
Usage: "Specify the local IP/port to bind to",
|
||||||
|
|||||||
@@ -17,11 +17,13 @@ import (
|
|||||||
"net/url"
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
"os/signal"
|
"os/signal"
|
||||||
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
"syscall"
|
"syscall"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
appbsky "github.com/bluesky-social/indigo/api/bsky"
|
appbsky "github.com/bluesky-social/indigo/api/bsky"
|
||||||
|
chat "github.com/bluesky-social/indigo/api/chat"
|
||||||
"github.com/bluesky-social/indigo/atproto/syntax"
|
"github.com/bluesky-social/indigo/atproto/syntax"
|
||||||
"github.com/bluesky-social/indigo/util/cliutil"
|
"github.com/bluesky-social/indigo/util/cliutil"
|
||||||
"github.com/bluesky-social/indigo/xrpc"
|
"github.com/bluesky-social/indigo/xrpc"
|
||||||
@@ -37,10 +39,11 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type Server struct {
|
type Server struct {
|
||||||
echo *echo.Echo
|
echo *echo.Echo
|
||||||
httpd *http.Server
|
httpd *http.Server
|
||||||
xrpcc *xrpc.Client
|
xrpcc *xrpc.Client
|
||||||
cfg *Config
|
chatXrpcc *xrpc.Client
|
||||||
|
cfg *Config
|
||||||
|
|
||||||
ipccClient http.Client
|
ipccClient http.Client
|
||||||
|
|
||||||
@@ -54,6 +57,7 @@ type Config struct {
|
|||||||
debug bool
|
debug bool
|
||||||
httpAddress string
|
httpAddress string
|
||||||
appviewHost string
|
appviewHost string
|
||||||
|
chatHost string
|
||||||
ogcardHost string
|
ogcardHost string
|
||||||
linkHost string
|
linkHost string
|
||||||
ipccHost string
|
ipccHost string
|
||||||
@@ -64,6 +68,7 @@ func serve(cctx *cli.Context) error {
|
|||||||
debug := cctx.Bool("debug")
|
debug := cctx.Bool("debug")
|
||||||
httpAddress := cctx.String("http-address")
|
httpAddress := cctx.String("http-address")
|
||||||
appviewHost := cctx.String("appview-host")
|
appviewHost := cctx.String("appview-host")
|
||||||
|
chatHost := cctx.String("chat-host")
|
||||||
ogcardHost := cctx.String("ogcard-host")
|
ogcardHost := cctx.String("ogcard-host")
|
||||||
linkHost := cctx.String("link-host")
|
linkHost := cctx.String("link-host")
|
||||||
ipccHost := cctx.String("ipcc-host")
|
ipccHost := cctx.String("ipcc-host")
|
||||||
@@ -83,6 +88,15 @@ func serve(cctx *cli.Context) error {
|
|||||||
Host: appviewHost,
|
Host: appviewHost,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// optional client for the chat appview, used by /c/<code> for OG previews.
|
||||||
|
var chatXrpcc *xrpc.Client
|
||||||
|
if chatHost != "" {
|
||||||
|
chatXrpcc = &xrpc.Client{
|
||||||
|
Client: cliutil.NewHttpClient(),
|
||||||
|
Host: chatHost,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// httpd
|
// httpd
|
||||||
var (
|
var (
|
||||||
httpTimeout = 2 * time.Minute
|
httpTimeout = 2 * time.Minute
|
||||||
@@ -106,12 +120,14 @@ func serve(cctx *cli.Context) error {
|
|||||||
// server
|
// server
|
||||||
//
|
//
|
||||||
server := &Server{
|
server := &Server{
|
||||||
echo: e,
|
echo: e,
|
||||||
xrpcc: xrpcc,
|
xrpcc: xrpcc,
|
||||||
|
chatXrpcc: chatXrpcc,
|
||||||
cfg: &Config{
|
cfg: &Config{
|
||||||
debug: debug,
|
debug: debug,
|
||||||
httpAddress: httpAddress,
|
httpAddress: httpAddress,
|
||||||
appviewHost: appviewHost,
|
appviewHost: appviewHost,
|
||||||
|
chatHost: chatHost,
|
||||||
ogcardHost: ogcardHost,
|
ogcardHost: ogcardHost,
|
||||||
linkHost: linkHost,
|
linkHost: linkHost,
|
||||||
ipccHost: ipccHost,
|
ipccHost: ipccHost,
|
||||||
@@ -349,6 +365,9 @@ func serve(cctx *cli.Context) error {
|
|||||||
e.GET("/starter-pack-short/:code", server.WebGeneric)
|
e.GET("/starter-pack-short/:code", server.WebGeneric)
|
||||||
e.GET("/start/:handleOrDID/:rkey", server.WebStarterPack)
|
e.GET("/start/:handleOrDID/:rkey", server.WebStarterPack)
|
||||||
|
|
||||||
|
// chat invites
|
||||||
|
e.GET("/c/:code", server.WebChatInvite)
|
||||||
|
|
||||||
// bookmarks
|
// bookmarks
|
||||||
e.GET("/saved", server.WebGeneric)
|
e.GET("/saved", server.WebGeneric)
|
||||||
|
|
||||||
@@ -644,6 +663,43 @@ func (srv *Server) WebStarterPack(c echo.Context) error {
|
|||||||
return c.Render(http.StatusOK, "starterpack.html", data)
|
return c.Render(http.StatusOK, "starterpack.html", data)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// chatInviteCodeRe is a permissive sanity check on the join code so we don't
|
||||||
|
// proxy obviously-bad input to the chat appview. Codes are opaque per the
|
||||||
|
// lexicon, but in practice URL-safe and short.
|
||||||
|
var chatInviteCodeRe = regexp.MustCompile(`^[A-Za-z0-9_-]{1,64}$`)
|
||||||
|
|
||||||
|
func (srv *Server) WebChatInvite(c echo.Context) error {
|
||||||
|
req := c.Request()
|
||||||
|
ctx := req.Context()
|
||||||
|
data := srv.NewTemplateContext()
|
||||||
|
data["requestURI"] = fmt.Sprintf("https://%s%s", req.Host, req.URL.Path)
|
||||||
|
|
||||||
|
code := c.Param("code")
|
||||||
|
if !chatInviteCodeRe.MatchString(code) {
|
||||||
|
return c.Render(http.StatusOK, "chatinvite.html", data)
|
||||||
|
}
|
||||||
|
if srv.chatXrpcc == nil {
|
||||||
|
return c.Render(http.StatusOK, "chatinvite.html", data)
|
||||||
|
}
|
||||||
|
|
||||||
|
out, err := chat.GroupGetJoinLinkPreviews(ctx, srv.chatXrpcc, []string{code})
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("failed to fetch chat invite preview for code=%s: %v", code, err)
|
||||||
|
return c.Render(http.StatusOK, "chatinvite.html", data)
|
||||||
|
}
|
||||||
|
if len(out.JoinLinkPreviews) == 0 {
|
||||||
|
return c.Render(http.StatusOK, "chatinvite.html", data)
|
||||||
|
}
|
||||||
|
preview := out.JoinLinkPreviews[0]
|
||||||
|
|
||||||
|
data["title"] = preview.Name
|
||||||
|
if srv.cfg.ogcardHost != "" {
|
||||||
|
// bskyogcard registers this route as /chat-invite/:code, not /c/:code.
|
||||||
|
data["imgThumbUrl"] = fmt.Sprintf("%s/chat-invite/%s", srv.cfg.ogcardHost, code)
|
||||||
|
}
|
||||||
|
return c.Render(http.StatusOK, "chatinvite.html", data)
|
||||||
|
}
|
||||||
|
|
||||||
func (srv *Server) WebProfile(c echo.Context) error {
|
func (srv *Server) WebProfile(c echo.Context) error {
|
||||||
ctx := c.Request().Context()
|
ctx := c.Request().Context()
|
||||||
data := srv.NewTemplateContext()
|
data := srv.NewTemplateContext()
|
||||||
|
|||||||
+1
-1
@@ -3,7 +3,7 @@ module github.com/bluesky-social/social-app/bskyweb
|
|||||||
go 1.26
|
go 1.26
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/bluesky-social/indigo v0.0.0-20260528134852-3e368bd18814
|
github.com/bluesky-social/indigo v0.0.0-20260529183052-5368f55344e0
|
||||||
github.com/flosch/pongo2/v6 v6.0.0
|
github.com/flosch/pongo2/v6 v6.0.0
|
||||||
github.com/ipfs/go-log v1.0.5
|
github.com/ipfs/go-log v1.0.5
|
||||||
github.com/joho/godotenv v1.5.1
|
github.com/joho/godotenv v1.5.1
|
||||||
|
|||||||
+2
-4
@@ -2,10 +2,8 @@ github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03
|
|||||||
github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
|
github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
|
||||||
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
||||||
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
||||||
github.com/bluesky-social/indigo v0.0.0-20260527160159-3b7634c713b8 h1:heYuihJZQZMNij7+dKIEfApG10xrGnDV2rtKpvFyYYU=
|
github.com/bluesky-social/indigo v0.0.0-20260529183052-5368f55344e0 h1:eijBaF59A5c+kPqufH7YO1GOqDMkyUhtM9P9aAWtfJY=
|
||||||
github.com/bluesky-social/indigo v0.0.0-20260527160159-3b7634c713b8/go.mod h1:JqQkz8lrOI6YZivP38GHmtVOTtzsNToITKj1gMpU5Jo=
|
github.com/bluesky-social/indigo v0.0.0-20260529183052-5368f55344e0/go.mod h1:JqQkz8lrOI6YZivP38GHmtVOTtzsNToITKj1gMpU5Jo=
|
||||||
github.com/bluesky-social/indigo v0.0.0-20260528134852-3e368bd18814 h1:UZK0qmB7LAe2IZY6RASlsE0Mm1Rjasddd2T5zWWvT54=
|
|
||||||
github.com/bluesky-social/indigo v0.0.0-20260528134852-3e368bd18814/go.mod h1:JqQkz8lrOI6YZivP38GHmtVOTtzsNToITKj1gMpU5Jo=
|
|
||||||
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
|
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
|
||||||
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||||
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
|
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
|
||||||
|
|||||||
@@ -0,0 +1,25 @@
|
|||||||
|
{% extends "base.html" %}
|
||||||
|
|
||||||
|
{% block html_head_extra -%}
|
||||||
|
{%- if requestURI %}
|
||||||
|
<meta property="og:url" content="{{ requestURI }}">
|
||||||
|
{% endif -%}
|
||||||
|
{%- if imgThumbUrl %}
|
||||||
|
<meta property="og:image" content="{{ imgThumbUrl }}">
|
||||||
|
<meta property="twitter:image" content="{{ imgThumbUrl }}">
|
||||||
|
{%- else -%}
|
||||||
|
<meta property="og:image" content="https://bsky.app/static/social-card-default-gradient.png" />
|
||||||
|
<meta property="twitter:image" content="https://bsky.app/static/social-card-default-gradient.png" />
|
||||||
|
{% endif -%}
|
||||||
|
<meta name="twitter:card" content="summary_large_image" />
|
||||||
|
{%- if title %}
|
||||||
|
<meta property="og:title" content="{{ title }}" />
|
||||||
|
<meta name="twitter:title" content="{{ title }}" />
|
||||||
|
{%- else -%}
|
||||||
|
<meta property="og:title" content="Bluesky" />
|
||||||
|
<meta name="twitter:title" content="Bluesky" />
|
||||||
|
{% endif -%}
|
||||||
|
<meta name="description" content="Join this group chat on Bluesky" />
|
||||||
|
<meta name="og:description" content="Join this group chat on Bluesky" />
|
||||||
|
<meta name="twitter:description" content="Join this group chat on Bluesky" />
|
||||||
|
{%- endblock %}
|
||||||
Reference in New Issue
Block a user