Scope knowledge and image libraries by brand
This commit is contained in:
@@ -809,6 +809,7 @@ func (s *EnterpriseSiteService) PublishArticle(ctx context.Context, connectionID
|
||||
contentHTML, coverURL := s.prepareEnterpriseSitePublishAssets(
|
||||
ctx,
|
||||
actor.TenantID,
|
||||
brandID,
|
||||
contentHTML,
|
||||
coverURLInput,
|
||||
coverImageAssetID,
|
||||
@@ -1865,20 +1866,20 @@ func enterpriseSitePublishCoverInput(article enterpriseSitePublishArticle, req P
|
||||
return coverURL, coverImageAssetID
|
||||
}
|
||||
|
||||
func (s *EnterpriseSiteService) prepareEnterpriseSitePublishAssets(ctx context.Context, tenantID int64, contentHTML, coverURL string, coverImageAssetID *int64) (string, string) {
|
||||
func (s *EnterpriseSiteService) prepareEnterpriseSitePublishAssets(ctx context.Context, tenantID int64, brandID int64, contentHTML, coverURL string, coverImageAssetID *int64) (string, string) {
|
||||
base := s.enterpriseSitePublicBaseURL()
|
||||
preparedHTML := s.prepareEnterpriseSiteImageURLs(ctx, tenantID, contentHTML, base)
|
||||
preparedCover := s.prepareEnterpriseSiteCoverURL(ctx, tenantID, coverURL, coverImageAssetID, base)
|
||||
preparedHTML := s.prepareEnterpriseSiteImageURLs(ctx, tenantID, brandID, contentHTML, base)
|
||||
preparedCover := s.prepareEnterpriseSiteCoverURL(ctx, tenantID, brandID, coverURL, coverImageAssetID, base)
|
||||
return preparedHTML, preparedCover
|
||||
}
|
||||
|
||||
func (s *EnterpriseSiteService) prepareEnterpriseSiteCoverURL(ctx context.Context, tenantID int64, coverURL string, coverImageAssetID *int64, baseURL string) string {
|
||||
func (s *EnterpriseSiteService) prepareEnterpriseSiteCoverURL(ctx context.Context, tenantID int64, brandID int64, coverURL string, coverImageAssetID *int64, baseURL string) string {
|
||||
if coverImageAssetID != nil && *coverImageAssetID > 0 {
|
||||
if dataURI := s.enterpriseSiteDataURIFromImageAssetID(ctx, tenantID, *coverImageAssetID); dataURI != "" {
|
||||
if dataURI := s.enterpriseSiteDataURIFromImageAssetID(ctx, tenantID, brandID, *coverImageAssetID); dataURI != "" {
|
||||
return dataURI
|
||||
}
|
||||
}
|
||||
return s.prepareEnterpriseSiteAssetURL(ctx, tenantID, coverURL, baseURL)
|
||||
return s.prepareEnterpriseSiteAssetURL(ctx, tenantID, brandID, coverURL, baseURL)
|
||||
}
|
||||
|
||||
func (s *EnterpriseSiteService) enterpriseSitePublicBaseURL() string {
|
||||
@@ -1918,7 +1919,7 @@ func normalizeEnterpriseSiteAssetBaseURL(value string) string {
|
||||
return strings.TrimRight(parsed.String(), "/")
|
||||
}
|
||||
|
||||
func (s *EnterpriseSiteService) prepareEnterpriseSiteImageURLs(ctx context.Context, tenantID int64, fragment string, baseURL string) string {
|
||||
func (s *EnterpriseSiteService) prepareEnterpriseSiteImageURLs(ctx context.Context, tenantID int64, brandID int64, fragment string, baseURL string) string {
|
||||
if strings.TrimSpace(fragment) == "" || !strings.Contains(strings.ToLower(fragment), "<img") {
|
||||
return fragment
|
||||
}
|
||||
@@ -1929,37 +1930,37 @@ func (s *EnterpriseSiteService) prepareEnterpriseSiteImageURLs(ctx context.Conte
|
||||
}
|
||||
nodes, err := html.ParseFragment(strings.NewReader(fragment), contextNode)
|
||||
if err != nil {
|
||||
return s.prepareEnterpriseSiteImageURLsFallback(ctx, tenantID, fragment, baseURL)
|
||||
return s.prepareEnterpriseSiteImageURLsFallback(ctx, tenantID, brandID, fragment, baseURL)
|
||||
}
|
||||
for _, node := range nodes {
|
||||
s.prepareEnterpriseSiteImageNodeURLs(ctx, tenantID, node, baseURL)
|
||||
s.prepareEnterpriseSiteImageNodeURLs(ctx, tenantID, brandID, node, baseURL)
|
||||
}
|
||||
nodes = enterpriseSitePruneEmptyImageWrapperNodes(nodes)
|
||||
return enterpriseSiteRenderHTMLNodes(nodes, s.prepareEnterpriseSiteImageURLsFallback(ctx, tenantID, fragment, baseURL))
|
||||
return enterpriseSiteRenderHTMLNodes(nodes, s.prepareEnterpriseSiteImageURLsFallback(ctx, tenantID, brandID, fragment, baseURL))
|
||||
}
|
||||
|
||||
func (s *EnterpriseSiteService) prepareEnterpriseSiteImageNodeURLs(ctx context.Context, tenantID int64, node *html.Node, baseURL string) {
|
||||
func (s *EnterpriseSiteService) prepareEnterpriseSiteImageNodeURLs(ctx context.Context, tenantID int64, brandID int64, node *html.Node, baseURL string) {
|
||||
if node == nil {
|
||||
return
|
||||
}
|
||||
if node.Type == html.ElementNode && strings.EqualFold(node.Data, "img") {
|
||||
if !s.prepareEnterpriseSiteImageAttrs(ctx, tenantID, node, baseURL) {
|
||||
if !s.prepareEnterpriseSiteImageAttrs(ctx, tenantID, brandID, node, baseURL) {
|
||||
enterpriseSiteRemoveHTMLNode(node)
|
||||
return
|
||||
}
|
||||
}
|
||||
for child := node.FirstChild; child != nil; {
|
||||
next := child.NextSibling
|
||||
s.prepareEnterpriseSiteImageNodeURLs(ctx, tenantID, child, baseURL)
|
||||
s.prepareEnterpriseSiteImageNodeURLs(ctx, tenantID, brandID, child, baseURL)
|
||||
child = next
|
||||
}
|
||||
}
|
||||
|
||||
func (s *EnterpriseSiteService) prepareEnterpriseSiteImageAttrs(ctx context.Context, tenantID int64, node *html.Node, baseURL string) bool {
|
||||
func (s *EnterpriseSiteService) prepareEnterpriseSiteImageAttrs(ctx context.Context, tenantID int64, brandID int64, node *html.Node, baseURL string) bool {
|
||||
if node == nil {
|
||||
return true
|
||||
}
|
||||
dataURI := s.enterpriseSiteDataURIFromImageNode(ctx, tenantID, node)
|
||||
dataURI := s.enterpriseSiteDataURIFromImageNode(ctx, tenantID, brandID, node)
|
||||
hasImageURL := false
|
||||
for idx := range node.Attr {
|
||||
key := strings.ToLower(strings.TrimSpace(node.Attr[idx].Key))
|
||||
@@ -1971,7 +1972,7 @@ func (s *EnterpriseSiteService) prepareEnterpriseSiteImageAttrs(ctx context.Cont
|
||||
node.Attr[idx].Val = dataURI
|
||||
continue
|
||||
}
|
||||
node.Attr[idx].Val = s.prepareEnterpriseSiteAssetURL(ctx, tenantID, node.Attr[idx].Val, baseURL)
|
||||
node.Attr[idx].Val = s.prepareEnterpriseSiteAssetURL(ctx, tenantID, brandID, node.Attr[idx].Val, baseURL)
|
||||
if strings.TrimSpace(node.Attr[idx].Val) == "" {
|
||||
return false
|
||||
}
|
||||
@@ -2046,12 +2047,12 @@ func enterpriseSitePruneEmptyImageWrapperNodes(nodes []*html.Node) []*html.Node
|
||||
return pruned
|
||||
}
|
||||
|
||||
func (s *EnterpriseSiteService) prepareEnterpriseSiteAssetURL(ctx context.Context, tenantID int64, value string, baseURL string) string {
|
||||
func (s *EnterpriseSiteService) prepareEnterpriseSiteAssetURL(ctx context.Context, tenantID int64, brandID int64, value string, baseURL string) string {
|
||||
value = strings.TrimSpace(value)
|
||||
if value == "" || strings.HasPrefix(strings.ToLower(value), "data:") {
|
||||
return value
|
||||
}
|
||||
if dataURI := s.enterpriseSiteDataURIFromAssetURL(ctx, tenantID, value); dataURI != "" {
|
||||
if dataURI := s.enterpriseSiteDataURIFromAssetURL(ctx, tenantID, brandID, value); dataURI != "" {
|
||||
return dataURI
|
||||
}
|
||||
if enterpriseSiteLooksLikeSaaSAssetURL(value) {
|
||||
@@ -2060,34 +2061,34 @@ func (s *EnterpriseSiteService) prepareEnterpriseSiteAssetURL(ctx context.Contex
|
||||
return enterpriseSiteAbsoluteAssetURL(value, baseURL)
|
||||
}
|
||||
|
||||
func (s *EnterpriseSiteService) prepareEnterpriseSiteImageURLsFallback(ctx context.Context, tenantID int64, fragment string, baseURL string) string {
|
||||
func (s *EnterpriseSiteService) prepareEnterpriseSiteImageURLsFallback(ctx context.Context, tenantID int64, brandID int64, fragment string, baseURL string) string {
|
||||
return enterpriseSiteImageAttrPattern.ReplaceAllStringFunc(fragment, func(match string) string {
|
||||
parts := enterpriseSiteImageAttrPattern.FindStringSubmatch(match)
|
||||
if len(parts) != 4 {
|
||||
return match
|
||||
}
|
||||
return parts[1] + s.prepareEnterpriseSiteAssetURL(ctx, tenantID, parts[2], baseURL) + parts[3]
|
||||
return parts[1] + s.prepareEnterpriseSiteAssetURL(ctx, tenantID, brandID, parts[2], baseURL) + parts[3]
|
||||
})
|
||||
}
|
||||
|
||||
func (s *EnterpriseSiteService) enterpriseSiteDataURIFromAssetURL(ctx context.Context, tenantID int64, value string) string {
|
||||
objectKey, ok := s.enterpriseSiteObjectKeyFromAssetURL(ctx, tenantID, value)
|
||||
func (s *EnterpriseSiteService) enterpriseSiteDataURIFromAssetURL(ctx context.Context, tenantID int64, brandID int64, value string) string {
|
||||
objectKey, ok := s.enterpriseSiteObjectKeyFromAssetURL(ctx, tenantID, brandID, value)
|
||||
if !ok || s == nil || s.storage == nil {
|
||||
return ""
|
||||
}
|
||||
return s.enterpriseSiteDataURIFromObjectKey(ctx, objectKey)
|
||||
}
|
||||
|
||||
func (s *EnterpriseSiteService) enterpriseSiteDataURIFromImageNode(ctx context.Context, tenantID int64, node *html.Node) string {
|
||||
func (s *EnterpriseSiteService) enterpriseSiteDataURIFromImageNode(ctx context.Context, tenantID int64, brandID int64, node *html.Node) string {
|
||||
assetID := enterpriseSiteImageNodeAssetID(node)
|
||||
if assetID <= 0 {
|
||||
return ""
|
||||
}
|
||||
return s.enterpriseSiteDataURIFromImageAssetID(ctx, tenantID, assetID)
|
||||
return s.enterpriseSiteDataURIFromImageAssetID(ctx, tenantID, brandID, assetID)
|
||||
}
|
||||
|
||||
func (s *EnterpriseSiteService) enterpriseSiteDataURIFromImageAssetID(ctx context.Context, tenantID, assetID int64) string {
|
||||
objectKey, ok := s.loadEnterpriseSiteImageAssetObjectKey(ctx, tenantID, assetID)
|
||||
func (s *EnterpriseSiteService) enterpriseSiteDataURIFromImageAssetID(ctx context.Context, tenantID, brandID, assetID int64) string {
|
||||
objectKey, ok := s.loadEnterpriseSiteImageAssetObjectKey(ctx, tenantID, brandID, assetID)
|
||||
if !ok || s == nil || s.storage == nil {
|
||||
return ""
|
||||
}
|
||||
@@ -2143,27 +2144,54 @@ func enterpriseSiteImageNodeAssetID(node *html.Node) int64 {
|
||||
return 0
|
||||
}
|
||||
|
||||
func (s *EnterpriseSiteService) enterpriseSiteObjectKeyFromAssetURL(ctx context.Context, tenantID int64, value string) (string, bool) {
|
||||
func (s *EnterpriseSiteService) enterpriseSiteObjectKeyFromAssetURL(ctx context.Context, tenantID int64, brandID int64, value string) (string, bool) {
|
||||
if objectKey, ok := enterpriseSiteObjectKeyFromPublicAssetURL(value, tenantID, s.enterpriseSiteAssetTokenSecret()); ok {
|
||||
if !s.enterpriseSiteImageObjectKeyBelongsToBrand(ctx, tenantID, brandID, objectKey) {
|
||||
return "", false
|
||||
}
|
||||
return objectKey, true
|
||||
}
|
||||
assetID, ok := enterpriseSiteAssetIDFromURL(value)
|
||||
if !ok {
|
||||
return "", false
|
||||
}
|
||||
return s.loadEnterpriseSiteImageAssetObjectKey(ctx, tenantID, assetID)
|
||||
return s.loadEnterpriseSiteImageAssetObjectKey(ctx, tenantID, brandID, assetID)
|
||||
}
|
||||
|
||||
func (s *EnterpriseSiteService) loadEnterpriseSiteImageAssetObjectKey(ctx context.Context, tenantID, assetID int64) (string, bool) {
|
||||
if s == nil || s.pool == nil || tenantID <= 0 || assetID <= 0 {
|
||||
func (s *EnterpriseSiteService) enterpriseSiteImageObjectKeyBelongsToBrand(ctx context.Context, tenantID, brandID int64, objectKey string) bool {
|
||||
if s == nil || s.pool == nil || !enterpriseSiteObjectKeyIsImageLibraryAsset(objectKey, tenantID) {
|
||||
return true
|
||||
}
|
||||
if tenantID <= 0 || brandID <= 0 || strings.TrimSpace(objectKey) == "" {
|
||||
return false
|
||||
}
|
||||
var exists bool
|
||||
if err := s.pool.QueryRow(ctx, `
|
||||
SELECT EXISTS (
|
||||
SELECT 1
|
||||
FROM image_assets
|
||||
WHERE tenant_id = $1
|
||||
AND brand_id = $2
|
||||
AND object_key = $3
|
||||
AND status = 'active'
|
||||
AND deleted_at IS NULL
|
||||
)
|
||||
`, tenantID, brandID, objectKey).Scan(&exists); err != nil {
|
||||
return false
|
||||
}
|
||||
return exists
|
||||
}
|
||||
|
||||
func (s *EnterpriseSiteService) loadEnterpriseSiteImageAssetObjectKey(ctx context.Context, tenantID, brandID, assetID int64) (string, bool) {
|
||||
if s == nil || s.pool == nil || tenantID <= 0 || brandID <= 0 || assetID <= 0 {
|
||||
return "", false
|
||||
}
|
||||
var objectKey string
|
||||
if err := s.pool.QueryRow(ctx, `
|
||||
SELECT object_key
|
||||
FROM image_assets
|
||||
WHERE id = $1 AND tenant_id = $2 AND status = 'active' AND deleted_at IS NULL
|
||||
`, assetID, tenantID).Scan(&objectKey); err != nil {
|
||||
SELECT object_key
|
||||
FROM image_assets
|
||||
WHERE id = $1 AND tenant_id = $2 AND brand_id = $3 AND status = 'active' AND deleted_at IS NULL
|
||||
`, assetID, tenantID, brandID).Scan(&objectKey); err != nil {
|
||||
return "", false
|
||||
}
|
||||
if !enterpriseSiteObjectKeyAllowedForTenant(objectKey, tenantID) {
|
||||
@@ -2264,6 +2292,14 @@ func enterpriseSiteObjectKeyAllowedForTenant(objectKey string, tenantID int64) b
|
||||
strings.HasPrefix(objectKey, tenantPrefix+"articles/")
|
||||
}
|
||||
|
||||
func enterpriseSiteObjectKeyIsImageLibraryAsset(objectKey string, tenantID int64) bool {
|
||||
if tenantID <= 0 {
|
||||
return false
|
||||
}
|
||||
tenantPrefix := "tenants/" + strconv.FormatInt(tenantID, 10) + "/"
|
||||
return strings.HasPrefix(strings.TrimSpace(objectKey), tenantPrefix+"images/")
|
||||
}
|
||||
|
||||
func enterpriseSiteAbsoluteImageURLs(fragment string, baseURL string) string {
|
||||
if strings.TrimSpace(fragment) == "" || !strings.Contains(strings.ToLower(fragment), "<img") {
|
||||
return fragment
|
||||
|
||||
Reference in New Issue
Block a user