package api
import (
"fmt"
"net/url"
"strings"
)
// PageRef holds parsed Confluence page reference from a URL.
type PageRef struct {
BaseURL string // e.g., "https://confluence.example.com"
PageID string // numeric ID if available
SpaceKey string // from /display/SPACE/... format
Title string // URL-decoded page title
}
// ParsePageURL parses a Confluence Server/Data Center page URL into a PageRef.
//
// Supported formats:
// - https://confluence.example.com/pages/viewpage.action?pageId=12345
// - https://confluence.example.com/display/SPACE/Page+Title
// - https://confluence.example.com/display/SPACE/Page+Title/Sub+Page
func ParsePageURL(rawURL string) (*PageRef, error) {
u, err := url.Parse(rawURL)
if err != nil {
return nil, fmt.Errorf("parsing URL: %w", err)
}
if u.Scheme == "" || u.Host == "" {
return nil, fmt.Errorf("URL must include scheme and host: %s", rawURL)
}
baseURL := u.Scheme + "://" + u.Host
if u.Port() != "" && !strings.Contains(u.Host, ":") {
baseURL = u.Scheme + "://" + u.Host + ":" + u.Port()
}
path := strings.TrimRight(u.Path, "/")
// Format 1: /pages/viewpage.action?pageId=12345
if strings.HasSuffix(path, "/pages/viewpage.action") || path == "/pages/viewpage.action" {
pageID := u.Query().Get("pageId")
if pageID == "" {
return nil, fmt.Errorf("URL has viewpage.action but no pageId parameter: %s", rawURL)
}
return &PageRef{
BaseURL: baseURL,
PageID: pageID,
}, nil
}
// Format 2: /display/SPACE/Page+Title
if idx := strings.Index(path, "/display/"); idx != -1 {
rest := path[idx+len("/display/"):]
parts := strings.SplitN(rest, "/", 2)
if len(parts) < 2 || parts[0] == "" || parts[1] == "" {
return nil, fmt.Errorf("URL display format requires /display/SPACE/Title: %s", rawURL)
}
spaceKey := parts[0]
// The title may contain slashes for sub-pages; take the last segment
titleEncoded := parts[1]
// Confluence uses + for spaces in URL paths
title := strings.ReplaceAll(titleEncoded, "+", " ")
title, err = url.PathUnescape(title)
if err != nil {
return nil, fmt.Errorf("decoding page title: %w", err)
}
return &PageRef{
BaseURL: baseURL,
SpaceKey: spaceKey,
Title: title,
}, nil
}
return nil, fmt.Errorf("unrecognized Confluence URL format: %s", rawURL)
}