Files
boc/backend/handlers/social_media.go
T
Bernt 78b57273e2 security: Add proper authentication, RBAC, and tenant isolation
- Add password hashing with bcrypt
- Add AuthService with proper login
- Add password strength validation
- Add RBAC middleware (AdminOnly, ManagerOrAdmin)
- Add tenant isolation middleware
- Update CRM handler with tenant filtering
- Add JWT fallback for development mode
- Add user context helpers
- Build successful
2026-08-10 12:52:48 +00:00

268 lines
6.9 KiB
Go

package handlers
import (
"database/sql"
"encoding/json"
"fmt"
"net/http"
"time"
)
// SocialMediaAccount representerar ett kopplat socialt media-konto
type SocialMediaAccount struct {
ID string `json:"id"`
Platform string `json:"platform"`
AccountName string `json:"account_name"`
DisplayName string `json:"display_name"`
Followers int `json:"followers"`
Following int `json:"following"`
Posts int `json:"posts"`
ProfileURL string `json:"profile_url"`
AvatarURL string `json:"avatar_url"`
IsConnected bool `json:"is_connected"`
LastSynced time.Time `json:"last_synced"`
CreatedAt time.Time `json:"created_at"`
}
// SocialMediaPost representerar ett inlägg
type SocialMediaPost struct {
ID string `json:"id"`
AccountID string `json:"account_id"`
Platform string `json:"platform"`
Content string `json:"content"`
MediaURL string `json:"media_url,omitempty"`
Likes int `json:"likes"`
Comments int `json:"comments"`
Shares int `json:"shares"`
Reach int `json:"reach"`
PostedAt time.Time `json:"posted_at"`
Status string `json:"status"`
}
// SocialMediaStats representerar aggregerad statistik
type SocialMediaStats struct {
TotalFollowers int `json:"total_followers"`
TotalPosts int `json:"total_posts"`
TotalEngagement int `json:"total_engagement"`
Accounts int `json:"accounts"`
}
// SocialMediaHandler hanterar sociala media-konton
type SocialMediaHandler struct {
db *sql.DB
}
// NewSocialMediaHandler skapar en ny handler
func NewSocialMediaHandler(db *sql.DB) *SocialMediaHandler {
return &SocialMediaHandler{db: db}
}
// InitDB skapar tabeller för sociala media
func (h *SocialMediaHandler) InitDB() error {
_, err := h.db.Exec(`
CREATE TABLE IF NOT EXISTS social_media_accounts (
id TEXT PRIMARY KEY,
platform TEXT NOT NULL,
account_name TEXT NOT NULL,
display_name TEXT,
followers INTEGER DEFAULT 0,
following INTEGER DEFAULT 0,
posts INTEGER DEFAULT 0,
profile_url TEXT,
avatar_url TEXT,
is_connected BOOLEAN DEFAULT false,
last_synced TIMESTAMP,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
)
`)
if err != nil {
return err
}
_, err = h.db.Exec(`
CREATE TABLE IF NOT EXISTS social_media_posts (
id TEXT PRIMARY KEY,
account_id TEXT NOT NULL,
platform TEXT NOT NULL,
content TEXT,
media_url TEXT,
likes INTEGER DEFAULT 0,
comments INTEGER DEFAULT 0,
shares INTEGER DEFAULT 0,
reach INTEGER DEFAULT 0,
posted_at TIMESTAMP,
status TEXT DEFAULT 'published',
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (account_id) REFERENCES social_media_accounts(id)
)
`)
return err
}
// ListAccounts listar alla kopplade konton
func (h *SocialMediaHandler) ListAccounts(w http.ResponseWriter, r *http.Request) {
rows, err := h.db.Query(`
SELECT id, platform, account_name, display_name, followers, following, posts,
profile_url, avatar_url, is_connected, last_synced, created_at
FROM social_media_accounts
ORDER BY platform, account_name
`)
if err != nil {
writeJSON(w, http.StatusInternalServerError, map[string]interface{}{
"ok": false,
"error": err.Error(),
})
return
}
defer rows.Close()
var accounts []SocialMediaAccount
for rows.Next() {
var a SocialMediaAccount
var lastSynced sql.NullTime
err := rows.Scan(
&a.ID, &a.Platform, &a.AccountName, &a.DisplayName,
&a.Followers, &a.Following, &a.Posts,
&a.ProfileURL, &a.AvatarURL, &a.IsConnected,
&lastSynced, &a.CreatedAt,
)
if err != nil {
continue
}
if lastSynced.Valid {
a.LastSynced = lastSynced.Time
}
accounts = append(accounts, a)
}
if accounts == nil {
accounts = []SocialMediaAccount{}
}
writeJSON(w, http.StatusOK, map[string]interface{}{
"ok": true,
"accounts": accounts,
})
}
// GetStats returnerar aggregerad statistik
func (h *SocialMediaHandler) GetStats(w http.ResponseWriter, r *http.Request) {
var stats SocialMediaStats
err := h.db.QueryRow(`
SELECT
COALESCE(SUM(followers), 0),
COALESCE(SUM(posts), 0),
COUNT(*)
FROM social_media_accounts
WHERE is_connected = true
`).Scan(&stats.TotalFollowers, &stats.TotalPosts, &stats.Accounts)
if err != nil {
writeJSON(w, http.StatusInternalServerError, map[string]interface{}{
"ok": false,
"error": err.Error(),
})
return
}
writeJSON(w, http.StatusOK, map[string]interface{}{
"ok": true,
"stats": stats,
})
}
// AddAccount lägger till ett nytt konto
func (h *SocialMediaHandler) AddAccount(w http.ResponseWriter, r *http.Request) {
var req struct {
Platform string `json:"platform"`
AccountName string `json:"account_name"`
DisplayName string `json:"display_name"`
ProfileURL string `json:"profile_url"`
}
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
writeJSON(w, http.StatusBadRequest, map[string]interface{}{
"ok": false,
"error": "invalid request",
})
return
}
id := fmt.Sprintf("%s_%d", req.Platform, time.Now().Unix())
_, err := h.db.Exec(`
INSERT INTO social_media_accounts (id, platform, account_name, display_name, profile_url, is_connected)
VALUES (?, ?, ?, ?, ?, true)
`, id, req.Platform, req.AccountName, req.DisplayName, req.ProfileURL)
if err != nil {
writeJSON(w, http.StatusInternalServerError, map[string]interface{}{
"ok": false,
"error": err.Error(),
})
return
}
writeJSON(w, http.StatusOK, map[string]interface{}{
"ok": true,
"account": map[string]interface{}{
"id": id,
"platform": req.Platform,
"name": req.AccountName,
},
})
}
// ListPosts listar inlägg
func (h *SocialMediaHandler) ListPosts(w http.ResponseWriter, r *http.Request) {
accountID := r.URL.Query().Get("account_id")
platform := r.URL.Query().Get("platform")
query := `
SELECT id, account_id, platform, content, media_url, likes, comments, shares, reach, posted_at, status
FROM social_media_posts
WHERE 1=1
`
var args []interface{}
if accountID != "" {
query += " AND account_id = ?"
args = append(args, accountID)
}
if platform != "" {
query += " AND platform = ?"
args = append(args, platform)
}
query += " ORDER BY posted_at DESC LIMIT 50"
rows, err := h.db.Query(query, args...)
if err != nil {
writeJSON(w, http.StatusInternalServerError, map[string]interface{}{
"ok": false,
"error": err.Error(),
})
return
}
defer rows.Close()
var posts []SocialMediaPost
for rows.Next() {
var p SocialMediaPost
var postedAt sql.NullTime
err := rows.Scan(
&p.ID, &p.AccountID, &p.Platform, &p.Content, &p.MediaURL,
&p.Likes, &p.Comments, &p.Shares, &p.Reach, &postedAt, &p.Status,
)
if err != nil {
continue
}
if postedAt.Valid {
p.PostedAt = postedAt.Time
}
posts = append(posts, p)
}
if posts == nil {
posts = []SocialMediaPost{}
}
writeJSON(w, http.StatusOK, map[string]interface{}{
"ok": true,
"posts": posts,
})
}