From 935876570b14d1f779f563b77fc29809c6fbc05d Mon Sep 17 00:00:00 2001 From: onysd Date: Mon, 13 Jul 2026 09:51:01 +0300 Subject: [PATCH] changed web preview --- .env.example | 4 + cmd/telesrv/main.go | 1 + internal/config/config.go | 18 +- internal/links/links.go | 1 + internal/rpc/sticker_link_smoke_test.go | 2 +- internal/web/assets.go | 51 ++ .../assets/fonts/plus-jakarta-sans-400.woff2 | Bin 0 -> 11876 bytes .../assets/fonts/plus-jakarta-sans-500.woff2 | Bin 0 -> 12380 bytes .../assets/fonts/plus-jakarta-sans-600.woff2 | Bin 0 -> 12228 bytes .../assets/fonts/plus-jakarta-sans-700.woff2 | Bin 0 -> 12364 bytes .../assets/fonts/plus-jakarta-sans-800.woff2 | Bin 0 -> 12044 bytes internal/web/assets/logo.png | Bin 0 -> 20880 bytes internal/web/server.go | 494 +++++++++++++----- internal/web/server_test.go | 7 +- 14 files changed, 444 insertions(+), 134 deletions(-) create mode 100644 internal/web/assets.go create mode 100644 internal/web/assets/fonts/plus-jakarta-sans-400.woff2 create mode 100644 internal/web/assets/fonts/plus-jakarta-sans-500.woff2 create mode 100644 internal/web/assets/fonts/plus-jakarta-sans-600.woff2 create mode 100644 internal/web/assets/fonts/plus-jakarta-sans-700.woff2 create mode 100644 internal/web/assets/fonts/plus-jakarta-sans-800.woff2 create mode 100644 internal/web/assets/logo.png diff --git a/.env.example b/.env.example index d543def3..2486e0b8 100644 --- a/.env.example +++ b/.env.example @@ -65,6 +65,10 @@ TELESRV_PUBLIC_APP_SCHEME=telesrv TELESRV_PUBLIC_WEB_BASE_URL=https://web.telesrv.net TELESRV_PUBLIC_APP_NAME=telesrv +# Product site/download page linked from the "Download" button in the public +# landing pages' header. +TELESRV_PUBLIC_DOWNLOAD_URL=https://owpengram.org + # Admin API / Admin UI 配置 # # TELESRV_ADMIN_API_TOKEN 是主服务 (cmd/telesrv) 暴露 Admin REST API 的鉴权 token, diff --git a/cmd/telesrv/main.go b/cmd/telesrv/main.go index 0bb03121..6632746d 100644 --- a/cmd/telesrv/main.go +++ b/cmd/telesrv/main.go @@ -825,6 +825,7 @@ func run(logger *zap.Logger) error { AppScheme: cfg.PublicAppScheme, WebBaseURL: cfg.PublicWebBaseURL, AppName: cfg.PublicAppName, + DownloadURL: cfg.PublicDownloadURL, StickerSets: filesService, Users: userStore, Channels: channelStore, diff --git a/internal/config/config.go b/internal/config/config.go index bfc690e0..1b639084 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -74,6 +74,8 @@ type Config struct { PublicWebBaseURL string // PublicAppName 是公开落地页展示的产品名,不参与协议路由。 PublicAppName string + // PublicDownloadURL 是公开落地页头部“Download”按钮指向的产品官网/下载页 URL。 + PublicDownloadURL string // PublicLinkWebAddr 是公开链接落地页监听地址;为空关闭。 // 生产应只监听 loopback,并由 nginx 将 /、/addstickers/、/addemoji/ 与 /addlist/ 反代到该地址。 PublicLinkWebAddr string @@ -372,14 +374,23 @@ func Load() (Config, error) { if err != nil { return Config{}, fmt.Errorf("TELESRV_PUBLIC_APP_SCHEME: %w", err) } - publicWebBaseURL, err := links.ValidateBaseURL(envOr("TELESRV_PUBLIC_WEB_BASE_URL", links.DefaultWebBaseURL)) - if err != nil { - return Config{}, fmt.Errorf("TELESRV_PUBLIC_WEB_BASE_URL: %w", err) + // TELESRV_PUBLIC_WEB_BASE_URL is nullable: an explicitly empty value disables + // the "Open in Web" button on public landing pages instead of falling back + // to the default telesrv Web client URL. + publicWebBaseURL := envAllowEmptyOr("TELESRV_PUBLIC_WEB_BASE_URL", links.DefaultWebBaseURL) + if publicWebBaseURL != "" { + if publicWebBaseURL, err = links.ValidateBaseURL(publicWebBaseURL); err != nil { + return Config{}, fmt.Errorf("TELESRV_PUBLIC_WEB_BASE_URL: %w", err) + } } publicAppName, err := links.ValidateAppName(envOr("TELESRV_PUBLIC_APP_NAME", links.DefaultAppName)) if err != nil { return Config{}, fmt.Errorf("TELESRV_PUBLIC_APP_NAME: %w", err) } + publicDownloadURL, err := links.ValidateBaseURL(envOr("TELESRV_PUBLIC_DOWNLOAD_URL", links.DefaultDownloadURL)) + if err != nil { + return Config{}, fmt.Errorf("TELESRV_PUBLIC_DOWNLOAD_URL: %w", err) + } cfg := Config{ ListenAddr: envOr("TELESRV_LISTEN", "0.0.0.0:2398"), @@ -416,6 +427,7 @@ func Load() (Config, error) { PublicAppScheme: publicAppScheme, PublicWebBaseURL: publicWebBaseURL, PublicAppName: publicAppName, + PublicDownloadURL: publicDownloadURL, PublicLinkWebAddr: envAllowEmptyOr("TELESRV_PUBLIC_LINK_WEB_ADDR", ""), AdminUIAddr: envOr("TELESRV_ADMIN_UI_ADDR", "127.0.0.1:2600"), AdminUIPassword: envOr("TELESRV_ADMIN_UI_PASSWORD", ""), diff --git a/internal/links/links.go b/internal/links/links.go index 50ac7a2f..b2ac34fa 100644 --- a/internal/links/links.go +++ b/internal/links/links.go @@ -11,6 +11,7 @@ const ( DefaultWebBaseURL = "https://web.telesrv.net" DefaultAppScheme = "telesrv" DefaultAppName = "telesrv" + DefaultDownloadURL = "https://owpengram.org" ) const MaxChatlistSlugBytes = 128 diff --git a/internal/rpc/sticker_link_smoke_test.go b/internal/rpc/sticker_link_smoke_test.go index 4c51b172..8c41cfa7 100644 --- a/internal/rpc/sticker_link_smoke_test.go +++ b/internal/rpc/sticker_link_smoke_test.go @@ -89,7 +89,7 @@ func TestCustomStickerPackLinkInstallAndSendSmoke(t *testing.T) { t.Fatalf("sticker link status = %d body=%q, want 200", rr.Code, rr.Body.String()) } body := rr.Body.String() - for _, want := range []string{"https://telesrv.net/addstickers/alice_fresh_pack", "telesrv://addstickers?set=alice_fresh_pack", "tg://addstickers?set=alice_fresh_pack"} { + for _, want := range []string{"https://telesrv.net/addstickers/alice_fresh_pack", "telesrv://addstickers?set=alice_fresh_pack"} { if !strings.Contains(body, want) { t.Fatalf("sticker link body missing %q:\n%s", want, body) } diff --git a/internal/web/assets.go b/internal/web/assets.go new file mode 100644 index 00000000..f23a9427 --- /dev/null +++ b/internal/web/assets.go @@ -0,0 +1,51 @@ +package web + +import ( + _ "embed" + "net/http" + "strings" +) + +//go:embed assets/logo.png +var brandLogoPNG []byte + +//go:embed assets/fonts/plus-jakarta-sans-400.woff2 +var fontJakarta400 []byte + +//go:embed assets/fonts/plus-jakarta-sans-500.woff2 +var fontJakarta500 []byte + +//go:embed assets/fonts/plus-jakarta-sans-600.woff2 +var fontJakarta600 []byte + +//go:embed assets/fonts/plus-jakarta-sans-700.woff2 +var fontJakarta700 []byte + +//go:embed assets/fonts/plus-jakarta-sans-800.woff2 +var fontJakarta800 []byte + +var publicFonts = map[string][]byte{ + "plus-jakarta-sans-400.woff2": fontJakarta400, + "plus-jakarta-sans-500.woff2": fontJakarta500, + "plus-jakarta-sans-600.woff2": fontJakarta600, + "plus-jakarta-sans-700.woff2": fontJakarta700, + "plus-jakarta-sans-800.woff2": fontJakarta800, +} + +func (h *handler) brandLogo(w http.ResponseWriter, _ *http.Request) { + w.Header().Set("Content-Type", "image/png") + w.Header().Set("Cache-Control", "public, max-age=604800, immutable") + _, _ = w.Write(brandLogoPNG) +} + +func (h *handler) brandFont(w http.ResponseWriter, r *http.Request) { + name := strings.TrimSpace(r.PathValue("file")) + data, ok := publicFonts[name] + if !ok { + http.NotFound(w, r) + return + } + w.Header().Set("Content-Type", "font/woff2") + w.Header().Set("Cache-Control", "public, max-age=31536000, immutable") + _, _ = w.Write(data) +} diff --git a/internal/web/assets/fonts/plus-jakarta-sans-400.woff2 b/internal/web/assets/fonts/plus-jakarta-sans-400.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..b3d513261684bdf0fd45e7b090c75b7445c1f953 GIT binary patch literal 11876 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 SyRXC+PkkZvdvUG}OaK7*r+Bjf literal 0 HcmV?d00001 diff --git a/internal/web/assets/fonts/plus-jakarta-sans-700.woff2 b/internal/web/assets/fonts/plus-jakarta-sans-700.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..18d8d152f646d1e80f38ec7710abe4229cd43b28 GIT binary patch literal 12364 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 literal 0 HcmV?d00001 diff --git a/internal/web/assets/fonts/plus-jakarta-sans-800.woff2 b/internal/web/assets/fonts/plus-jakarta-sans-800.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..91be461a2decfd421927f419ded6c3d42ce55e04 GIT binary patch literal 12044 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 literal 0 HcmV?d00001 diff --git a/internal/web/assets/logo.png b/internal/web/assets/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..323390560e6571c39606033315d089d00e089b9b GIT binary patch literal 20880 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 }(?; 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 literal 0 HcmV?d00001 diff --git a/internal/web/server.go b/internal/web/server.go index 9cbcad7f..7f47f8f5 100644 --- a/internal/web/server.go +++ b/internal/web/server.go @@ -26,6 +26,7 @@ type Config struct { AppScheme string WebBaseURL string AppName string + DownloadURL string StickerSets StickerSetResolver Users UsernameResolver Channels PublicChannelResolver @@ -87,7 +88,8 @@ func Start(ctx context.Context, cfg Config, logger *zap.Logger) (*http.Server, e zap.String("addr", addr), zap.String("public_base_url", cfg.PublicBaseURL), zap.String("app_scheme", cfg.AppScheme), - zap.String("web_base_url", cfg.WebBaseURL)) + zap.String("web_base_url", cfg.WebBaseURL), + zap.String("download_url", cfg.DownloadURL)) if err := srv.Serve(ln); err != nil && !errors.Is(err, http.ErrServerClosed) { logger.Warn("Public link Web endpoint exited", zap.Error(err)) } @@ -110,9 +112,6 @@ func newHandler(cfg Config, logger *zap.Logger) (http.Handler, error) { if cfg.StickerSets == nil { return nil, fmt.Errorf("public Web sticker set resolver is nil") } - if strings.TrimSpace(cfg.WebBaseURL) == "" { - cfg.WebBaseURL = links.DefaultWebBaseURL - } if strings.TrimSpace(cfg.AppName) == "" { cfg.AppName = links.DefaultAppName } @@ -122,12 +121,25 @@ func newHandler(cfg Config, logger *zap.Logger) (http.Handler, error) { if cfg.AppScheme, err = links.ValidateAppScheme(cfg.AppScheme); err != nil { return nil, fmt.Errorf("app scheme: %w", err) } - if cfg.WebBaseURL, err = links.ValidateBaseURL(cfg.WebBaseURL); err != nil { - return nil, fmt.Errorf("Web base URL: %w", err) + // WebBaseURL is optional: leaving it unset disables the "Open in Web" + // button on the username landing page instead of falling back to a + // default URL that would point at someone else's Web client. + if strings.TrimSpace(cfg.WebBaseURL) != "" { + if cfg.WebBaseURL, err = links.ValidateBaseURL(cfg.WebBaseURL); err != nil { + return nil, fmt.Errorf("Web base URL: %w", err) + } + } else { + cfg.WebBaseURL = "" } if cfg.AppName, err = links.ValidateAppName(cfg.AppName); err != nil { return nil, fmt.Errorf("app name: %w", err) } + if strings.TrimSpace(cfg.DownloadURL) == "" { + cfg.DownloadURL = links.DefaultDownloadURL + } + if cfg.DownloadURL, err = links.ValidateBaseURL(cfg.DownloadURL); err != nil { + return nil, fmt.Errorf("download URL: %w", err) + } if logger == nil { logger = zap.NewNop() } @@ -141,10 +153,13 @@ func newHandler(cfg Config, logger *zap.Logger) (http.Handler, error) { appScheme: cfg.AppScheme, webBaseURL: cfg.WebBaseURL, appName: cfg.AppName, + downloadURL: cfg.DownloadURL, logger: logger, } mux := http.NewServeMux() mux.HandleFunc("GET /healthz", h.healthz) + mux.HandleFunc("GET /_public/assets/logo.png", h.brandLogo) + mux.HandleFunc("GET /_public/assets/fonts/{file}", h.brandFont) mux.HandleFunc("GET /_public/avatar/{username}/{photoID}", h.publicAvatar) mux.HandleFunc("GET /addstickers/{shortName}", h.addStickers) mux.HandleFunc("GET /addemoji/{shortName}", h.addEmoji) @@ -164,6 +179,7 @@ type handler struct { appScheme string webBaseURL string appName string + downloadURL string logger *zap.Logger } @@ -195,9 +211,8 @@ func (h *handler) addList(w http.ResponseWriter, r *http.Request) { Description: "This page opens the app so you can preview and add this shared folder.", CanonicalURL: h.publicURL("addlist", slug), AppURL: template.URL(app), - LegacyTgURL: template.URL(legacyTgURL("addlist", "slug", slug)), + DownloadURL: h.downloadURL, } - data.AppURLJS = template.JS(strconv.Quote(app)) w.Header().Set("Content-Type", "text/html; charset=utf-8") w.Header().Set("Cache-Control", "public, max-age=60") if err := landingTemplate.Execute(w, data); err != nil { @@ -228,11 +243,11 @@ func (h *handler) usernameLink(w http.ResponseWriter, r *http.Request) { } params.Set("domain", peer.username) app := schemeURLValues(h.appScheme, "resolve", params) - legacy := schemeURLValues("tg", "resolve", params) description := peer.about if description == "" { description = peer.fallbackDescription(h.appName) } + grad := avatarGradient(peer.id) data := usernamePageData{ AppName: h.appName, AppInitial: appInitial(h.appName), @@ -241,18 +256,23 @@ func (h *handler) usernameLink(w http.ResponseWriter, r *http.Request) { Verified: peer.verified, Extra: peer.extra(), Description: description, + AboutText: peer.about, CanonicalURL: h.publicUsernameURL(peer.username), HomeURL: h.publicBaseURL + "/", + DownloadURL: h.downloadURL, AppURL: template.URL(app), - LegacyTgURL: template.URL(legacy), - WebURL: template.URL(publicWebAppURL(h.webBaseURL, legacy)), ButtonLabel: peer.buttonLabel(), Initials: peer.initials(), + GradFrom: grad[0], + GradTo: grad[1], + } + if h.webBaseURL != "" { + legacy := schemeURLValues("tg", "resolve", params) + data.WebURL = template.URL(publicWebAppURL(h.webBaseURL, legacy)) } if peer.hasPhoto { data.PhotoURL = h.publicAvatarURL(peer.username, peer.photo.ID) } - data.AppURLJS = template.JS(strconv.Quote(app)) w.Header().Set("Content-Type", "text/html; charset=utf-8") w.Header().Set("Cache-Control", "public, max-age=60, must-revalidate") if err := usernameLandingTemplate.Execute(w, data); err != nil { @@ -366,9 +386,8 @@ func (h *handler) serveSet(w http.ResponseWriter, r *http.Request, pathKind stri Description: "This page opens the app so you can preview and install the set. Files are still fetched by the app through MTProto.", CanonicalURL: h.publicURL(canonicalKind, set.ShortName), AppURL: template.URL(app), - LegacyTgURL: template.URL(legacyTgURL(canonicalKind, "set", set.ShortName)), + DownloadURL: h.downloadURL, } - data.AppURLJS = template.JS(strconv.Quote(app)) w.Header().Set("Content-Type", "text/html; charset=utf-8") w.Header().Set("Cache-Control", "public, max-age=60") if err := landingTemplate.Execute(w, data); err != nil { @@ -398,6 +417,7 @@ const ( ) type publicPeer struct { + id int64 kind publicPeerKind username string title string @@ -470,6 +490,7 @@ func (h *handler) publicUserPeer(ctx context.Context, requested string, u domain } } peer := publicPeer{ + id: u.ID, kind: publicPeerUser, username: u.Username, title: title, @@ -501,6 +522,7 @@ func (h *handler) publicChannelPeer(ctx context.Context, requested string, ch do return publicPeer{}, false, fmt.Errorf("invalid public channel %q: %w", ch.Username, err) } peer := publicPeer{ + id: ch.ID, kind: publicPeerChannel, username: ch.Username, title: strings.TrimSpace(ch.Title), @@ -598,6 +620,23 @@ func (p publicPeer) initials() string { return strings.ToUpper(string(out)) } +var publicAvatarGradients = [...][2]string{ + {"#FF885E", "#FF516A"}, + {"#FFCD6A", "#FFA85C"}, + {"#82B1FF", "#665FFF"}, + {"#A0DE7E", "#54CB68"}, + {"#53EDD6", "#28C9B7"}, + {"#72D5FD", "#2A9EF1"}, + {"#E0A2F3", "#D669ED"}, +} + +func avatarGradient(id int64) [2]string { + if id < 0 { + id = -id + } + return publicAvatarGradients[id%int64(len(publicAvatarGradients))] +} + func groupedDecimal(n int) string { if n < 0 { n = 0 @@ -734,7 +773,7 @@ func publicWebAppURL(webBaseURL, legacyURL string) string { func publicSecurityHeaders(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - w.Header().Set("Content-Security-Policy", "default-src 'none'; img-src 'self' data:; style-src 'unsafe-inline'; script-src 'unsafe-inline'; base-uri 'none'; form-action 'none'; frame-ancestors 'none'") + w.Header().Set("Content-Security-Policy", "default-src 'none'; img-src 'self' data:; font-src 'self'; style-src 'unsafe-inline'; script-src 'unsafe-inline'; base-uri 'none'; form-action 'none'; frame-ancestors 'none'") w.Header().Set("Permissions-Policy", "camera=(), microphone=(), geolocation=(), payment=(), usb=()") w.Header().Set("Referrer-Policy", "no-referrer") w.Header().Set("X-Content-Type-Options", "nosniff") @@ -824,10 +863,6 @@ func (h *handler) appURL(kind, key, value string) string { return schemeURL(h.appScheme, kind, key, value) } -func legacyTgURL(kind, key, value string) string { - return schemeURL("tg", kind, key, value) -} - func schemeURL(scheme, kind, key, value string) string { return scheme + "://" + kind + "?" + key + "=" + url.QueryEscape(value) } @@ -839,9 +874,8 @@ type pageData struct { Subtitle string Description string CanonicalURL string + DownloadURL string AppURL template.URL - LegacyTgURL template.URL - AppURLJS template.JS } type usernamePageData struct { @@ -852,15 +886,17 @@ type usernamePageData struct { Verified bool Extra string Description string + AboutText string CanonicalURL string HomeURL string + DownloadURL string PhotoURL string Initials string + GradFrom string + GradTo string ButtonLabel string AppURL template.URL - LegacyTgURL template.URL WebURL template.URL - AppURLJS template.JS } func (h *handler) serveUsernameNotFound(w http.ResponseWriter, username string) { @@ -868,24 +904,244 @@ func (h *handler) serveUsernameNotFound(w http.ResponseWriter, username string) w.Header().Set("Cache-Control", "public, max-age=30, must-revalidate") w.WriteHeader(http.StatusNotFound) if err := usernameNotFoundTemplate.Execute(w, struct { - Username string - HomeURL string - AppName string - }{Username: username, HomeURL: h.publicBaseURL + "/", AppName: h.appName}); err != nil { + Username string + HomeURL string + AppName string + DownloadURL string + }{Username: username, HomeURL: h.publicBaseURL + "/", AppName: h.appName, DownloadURL: h.downloadURL}); err != nil { h.logger.Error("Render public username not-found page failed", zap.String("username", username), zap.Error(err)) } } +// publicPageStyleSheet is the shared visual system for every public landing +// page (username profile, not-found, sticker/addlist): CSS variables (light +// theme + dark-scheme override), the card/avatar/button component classes, +// and the floating background orbs. Fonts are self-hosted (no Google Fonts +// CDN call from visitors' browsers) via the /_public/assets/fonts/ route. +const publicPageStyleSheet = ` + @font-face{font-family:'Plus Jakarta Sans';font-style:normal;font-weight:400;font-display:swap;src:url('/_public/assets/fonts/plus-jakarta-sans-400.woff2') format('woff2')} + @font-face{font-family:'Plus Jakarta Sans';font-style:normal;font-weight:500;font-display:swap;src:url('/_public/assets/fonts/plus-jakarta-sans-500.woff2') format('woff2')} + @font-face{font-family:'Plus Jakarta Sans';font-style:normal;font-weight:600;font-display:swap;src:url('/_public/assets/fonts/plus-jakarta-sans-600.woff2') format('woff2')} + @font-face{font-family:'Plus Jakarta Sans';font-style:normal;font-weight:700;font-display:swap;src:url('/_public/assets/fonts/plus-jakarta-sans-700.woff2') format('woff2')} + @font-face{font-family:'Plus Jakarta Sans';font-style:normal;font-weight:800;font-display:swap;src:url('/_public/assets/fonts/plus-jakarta-sans-800.woff2') format('woff2')} + :root{ + --accent:#2563eb; --accent-bright:#38bdf8; + --grad:linear-gradient(135deg,#38bdf8 0%,#2563eb 55%,#1e40af 100%); + --bg:#f7f9fc; --surface:#ffffff; --surface-2:#f1f5f9; --line:#e2e8f0; --line-strong:#cbd5e1; + --ink:#050508; --body:#3f4b5c; --muted:#64748b; + --hero-glow:rgba(37,99,235,0.14); --hero-grid:rgba(5,5,8,0.04); + --radius:18px; --radius-lg:24px; + --shadow:0 28px 70px -36px rgba(5,5,8,0.35); + } + @media (prefers-color-scheme: dark){ + :root{ + --accent:#60a5fa; --accent-bright:#7dd3fc; + --grad:linear-gradient(135deg,#7dd3fc 0%,#3b82f6 50%,#2563eb 100%); + --bg:#08080e; --surface:#12121a; --surface-2:#181822; --line:#222228; --line-strong:#2e2e36; + --ink:#f8fafc; --body:#a8b4c4; --muted:#7b8798; + --hero-glow:rgba(59,130,246,0.25); --hero-grid:rgba(255,255,255,0.04); + --shadow:0 32px 80px -40px rgba(0,0,0,0.75); + } + } + *{box-sizing:border-box} + html,body{height:100%} + body{ + margin:0; color:var(--body); background:var(--bg); + font-family:'Plus Jakarta Sans',-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Helvetica,Arial,sans-serif; + -webkit-font-smoothing:antialiased; line-height:1.65; + min-height:100vh; display:flex; flex-direction:column; + background-image: + radial-gradient(900px 480px at 50% -8%, var(--hero-glow) 0%, rgba(0,0,0,0) 70%), + linear-gradient(var(--hero-grid) 1px, transparent 1px), + linear-gradient(90deg, var(--hero-grid) 1px, transparent 1px); + background-size:auto, 44px 44px, 44px 44px; + background-attachment:fixed; + } + h1,h2,h3{color:var(--ink); letter-spacing:-0.03em; font-weight:800; margin:0} + a{color:var(--accent); text-decoration:none} + header{ + display:flex; align-items:center; justify-content:space-between; + padding:18px 26px; max-width:1100px; width:100%; margin:0 auto; + } + .brand{display:flex; align-items:center; gap:11px; font-weight:800; font-size:19px; color:var(--ink); letter-spacing:-0.02em} + .brand img{width:34px; height:34px; border-radius:9px; display:block} + .btn{ + display:inline-flex; align-items:center; justify-content:center; gap:8px; + font-weight:700; font-size:0.92rem; padding:12px 20px; border-radius:11px; + border:none; cursor:pointer; text-decoration:none; line-height:1; transition:transform .16s ease, box-shadow .16s ease, background .16s ease, border-color .16s ease, color .16s ease; + font-family:inherit; + } + .btn:hover{transform:translateY(-1px)} + .btn-primary{background:var(--grad); color:#fff; box-shadow:0 12px 28px -14px rgba(37,99,235,0.65); width:100%} + .btn-primary:hover{box-shadow:0 16px 36px -14px rgba(37,99,235,0.75)} + .btn-ghost{background:var(--surface); color:var(--ink); border:1px solid var(--line)} + .btn-ghost:hover{border-color:var(--accent); color:var(--accent)} + main{flex:1; display:flex; align-items:center; justify-content:center; padding:24px} + .card{ + width:100%; max-width:430px; background:var(--surface); + border:1px solid var(--line); border-radius:var(--radius-lg); padding:38px 30px 30px; + text-align:center; box-shadow:var(--shadow); + } + .avatar-wrap{position:relative; width:124px; height:124px; margin:0 auto 20px} + .avatar{position:absolute; inset:0; width:124px; height:124px; border-radius:50%; display:block} + .avatar-photo{object-fit:cover; background:var(--surface-2)} + .name{font-size:25px; font-weight:800; line-height:1.2; color:var(--ink); word-break:break-word; display:inline-flex; align-items:center; gap:8px; justify-content:center} + .badge{width:20px; height:20px; flex:0 0 auto} + .sub{color:var(--muted); font-size:15px; margin-top:6px; font-weight:500} + .about{color:var(--body); font-size:15px; line-height:1.6; margin-top:18px; white-space:pre-wrap; word-break:break-word; text-align:center} + .actions{margin-top:26px; display:grid; gap:10px} + .scam{display:inline-block; margin-top:12px; color:#e5484d; border:1px solid rgba(229,72,77,0.25); background:rgba(229,72,77,0.08); font-size:12px; font-weight:700; letter-spacing:.4px; padding:4px 10px; border-radius:8px} + .nf-emoji{font-size:60px; line-height:1; margin-bottom:14px} + @media (max-width:480px){ .card{padding:30px 22px 24px} header{padding:16px 18px} } + + /* Floating background orbs. */ + .bg-orbs{position:fixed; inset:-60px; z-index:-1; pointer-events:none; transition:transform .3s ease-out; will-change:transform} + .bg-orb{position:absolute; border-radius:50%; filter:blur(100px); pointer-events:none} + .bg-orb--1{width:700px; height:700px; top:-15%; left:-10%; background:color-mix(in srgb,var(--accent-bright) 40%,transparent); animation:orbFloat1 20s ease-in-out infinite} + .bg-orb--2{width:600px; height:600px; top:25%; right:-15%; background:color-mix(in srgb,var(--accent) 38%,transparent); animation:orbFloat2 24s ease-in-out infinite} + .bg-orb--3{width:500px; height:500px; bottom:-15%; left:30%; background:color-mix(in srgb,var(--accent-bright) 30%,transparent); animation:orbFloat3 28s ease-in-out infinite} + @keyframes orbFloat1{0%,100%{transform:translate(0,0) scale(1)}33%{transform:translate(60px,-40px) scale(1.08)}66%{transform:translate(-30px,30px) scale(.92)}} + @keyframes orbFloat2{0%,100%{transform:translate(0,0) scale(1)}33%{transform:translate(-50px,-35px) scale(.93)}66%{transform:translate(45px,25px) scale(1.07)}} + @keyframes orbFloat3{0%,100%{transform:translate(0,0) scale(1)}33%{transform:translate(40px,45px) scale(1.06)}66%{transform:translate(-55px,-25px) scale(.94)}} + @media (max-width:720px){ .bg-orb{filter:blur(60px)} .bg-orb--1{width:350px;height:350px} .bg-orb--2{width:300px;height:300px} .bg-orb--3{width:250px;height:250px} } + @media (prefers-reduced-motion: reduce){ .bg-orb{animation:none} } + + /* Drifting line-icon particles. */ + .bg-icons{position:fixed; inset:0; z-index:-1; overflow:hidden; pointer-events:none; color:var(--accent)} + .bg-icon{position:absolute; left:0; top:0; will-change:transform} +` + +// publicBgOrbsMarkup is the floating-orb + particle container markup, placed +// right after on every public landing page. +const publicBgOrbsMarkup = ` + + ` + +// publicBgIconsScript drives the orb parallax and drifting line-icon +// particles. Self-contained vanilla JS, no external requests. +const publicBgIconsScript = ` + ` + var usernameLandingTemplate = template.Must(template.New("username-landing").Parse(` - + {{.Title}} (@{{.Username}}) - {{.AppName}} + @@ -898,123 +1154,113 @@ var usernameLandingTemplate = template.Must(template.New("username-landing").Par {{if .PhotoURL}}{{end}} - - -
- {{.AppInitial}}{{.AppName}} -
-
-
{{if .PhotoURL}}{{.Title}} profile photo{{else}}{{end}}
-

{{.Title}}{{if .Verified}}{{end}}

-

@{{.Username}}

- {{if .Extra}}

{{.Extra}}

{{end}} -

{{.Description}}

- -

Old test clients only: open with tg://

-
-
-
If you have {{.AppName}}, this page can open the chat directly.
-
- +` + publicBgOrbsMarkup + ` +
+ + {{.AppName}} + {{.AppName}} + + Download +
+
+
+
+ + {{if .PhotoURL}}{{end}} +
+
+ {{.Title}} + {{if .Verified}}{{end}} +
+
@{{.Username}}
+ {{if .Extra}}
{{.Extra}}
{{end}} + {{if .AboutText}}
{{.AboutText}}
{{end}} +
+ {{.ButtonLabel}} + {{if .WebURL}}Open in Web{{end}} +
+
+
` + publicBgIconsScript + ` `)) var usernameNotFoundTemplate = template.Must(template.New("username-not-found").Parse(` - -Username not found - {{.AppName}} - -

Username not found

{{if .Username}}@{{.Username}} is not an active public {{.AppName}} username.{{else}}This is not a valid public {{.AppName}} username.{{end}}

Back to {{.AppName}}
`)) + + + + + + + Username not found - {{.AppName}} + + + +` + publicBgOrbsMarkup + ` +
+ + {{.AppName}} + {{.AppName}} + + Download +
+
+
+
🤔
+

Nothing found

+
{{if .Username}}@{{.Username}} is not an active public {{.AppName}} username.{{else}}This is not a valid public {{.AppName}} username.{{end}}
+ +
+
` + publicBgIconsScript + ` + + +`)) var landingTemplate = template.Must(template.New("landing").Parse(` - + + {{.Title}} - {{.AppName}} + - - +` + publicBgOrbsMarkup + ` +
+ + {{.AppName}} + {{.AppName}} + + Download +
-

{{.KindLabel}}

-

{{.Title}}

-

{{.Subtitle}}

-

Open in {{.AppName}}

-

{{.Description}}

-

Old test clients only: open with tg://

-

{{.CanonicalURL}}

-
- +
+
{{.KindLabel}}
+

{{.Title}}

+ {{if .Subtitle}}
{{.Subtitle}}
{{end}} + {{if .Description}}
{{.Description}}
{{end}} + +
+ ` + publicBgIconsScript + ` `)) diff --git a/internal/web/server_test.go b/internal/web/server_test.go index 055fca32..31ce7d01 100644 --- a/internal/web/server_test.go +++ b/internal/web/server_test.go @@ -67,7 +67,6 @@ func TestHandlerServesStickerSetLandingPage(t *testing.T) { "Fresh Pack", "https://telesrv.net/addstickers/fresh_pack", "telesrv://addstickers?set=fresh_pack", - "tg://addstickers?set=fresh_pack", "Files are still fetched by the app through MTProto.", } { if !strings.Contains(body, want) { @@ -106,7 +105,6 @@ func TestHandlerServesEmojiLandingPage(t *testing.T) { "custom emoji set", "https://example.test/base/addemoji/emoji_pack", "telesrv://addemoji?set=emoji_pack", - "tg://addemoji?set=emoji_pack", } { if !strings.Contains(body, want) { t.Fatalf("body missing %q:\n%s", want, body) @@ -128,7 +126,6 @@ func TestHandlerServesChatlistLandingPage(t *testing.T) { "Shared Folder", "http://127.0.0.1:2401/addlist/zNhytIbwRwjaC2GH", "telesrv://addlist?slug=zNhytIbwRwjaC2GH", - "tg://addlist?slug=zNhytIbwRwjaC2GH", "preview and add this shared folder", } { if !strings.Contains(body, want) { @@ -164,7 +161,6 @@ func TestHandlerServesBotUsernameLandingPage(t *testing.T) { "@TetrisBot", "http://127.0.0.1:2401/TetrisBot", "telesrv://resolve?domain=TetrisBot", - "tg://resolve?domain=TetrisBot", "Start Bot", "Open telesrv to start a chat with this bot.", `property="og:title" content="Tetris Bot"`, @@ -323,7 +319,7 @@ func TestHandlerServesUserChannelAndSupergroupLandingPages(t *testing.T) { t.Fatalf("body missing %q:\n%s", want, rr.Body.String()) } } - if tc.path == "/aLiCe/" && strings.Count(rr.Body.String(), `

@Alice

`) != 1 { + if tc.path == "/aLiCe/" && strings.Count(rr.Body.String(), `
@Alice
`) != 1 { t.Fatalf("ordinary user username rendered more than once:\n%s", rr.Body.String()) } if strings.Contains(rr.Body.String(), "+15551234567") || strings.Contains(rr.Body.String(), "987654321") || strings.Contains(rr.Body.String(), "1700000000") { @@ -345,7 +341,6 @@ func TestHandlerPreservesBoundedResolveQueryAndOverridesDomain(t *testing.T) { body := rr.Body.String() for _, want := range []string{ "telesrv://resolve?domain=TetrisBot&ref=campaign&start=hello", - "tg://resolve?domain=TetrisBot&ref=campaign&start=hello", } { if !strings.Contains(body, want) { t.Fatalf("body missing sanitized query %q:\n%s", want, body)