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) }