Files
zeekay 1943f1c093 Hanzo Commerce
Multi-tenant commerce and billing: orders, subscriptions, invoices, plans,
usage metering and a credit ledger. One Go binary. MIT OR Apache-2.0.

Claude-Session: https://claude.ai/code/session_01SAtaDFm8VDiWrhYHNNtxMw
2026-09-23 21:52:16 -07:00

222 lines
5.3 KiB
Go

package facebook
import (
"errors"
"fmt"
"io/ioutil"
"math/rand"
"net/http"
"net/url"
"time"
fb "github.com/huandu/facebook"
"github.com/zap-proto/zip"
"github.com/hanzoai/commerce/config"
"github.com/hanzoai/commerce/util/cache"
// "github.com/hanzoai/commerce/models"
"github.com/hanzoai/commerce/log"
)
var appId = config.Facebook.AppId
var appSecret = config.Facebook.AppSecret
var _redirectUri string
// Sets _redirectUri as necessary for dev machines
// Uses config.UrlFor in production and staging.
func redirectUri(c *zip.Ctx) string {
// if config.IsDevelopment && _redirectUri == "" {
// client := urlfetch.Client(c.Context())
// req, _ := http.NewRequest("GET", "http://checkip.amazonaws.com", nil)
// res, _ := client.Do(req)
// defer res.Body.Close()
// b, _ := ioutil.ReadAll(res.Body)
// ip := string(b)
// ip = ip[0 : len(ip)-1]
// _redirectUri = "http://" + ip + ":8080/store/auth/facebook_callback"
// } else if (config.IsProduction || config.IsStaging) && _redirectUri == "" {
// _redirectUri = url.QueryEscape("http://" + config.UrlFor("store", "/auth/facebook_callback/"))
// }
// return _redirectUri
return ""
}
var graphVersion = config.Facebook.GraphVersion
var app *fb.App
func newSession(c *zip.Ctx, accessToken string) *fb.Session {
if app == nil {
app = fb.New(appId, appSecret)
app.RedirectUri = redirectUri(c)
}
session := app.Session(accessToken)
session.HttpClient = &http.Client{Timeout: 55 * time.Second}
return session
}
// GET /auth/facebook_callback
func Callback(c *zip.Ctx) error {
// req := c.Request
// e := req.URL.Query().Get("error")
// if e != "" {
// description := req.URL.Query().Get("error_description")
// reason := req.URL.Query().Get("error_reason")
// log.Info(
// "Error in facebook callback \n %s \n %s \n %s",
// e, reason, description,
// )
// return
// }
// if resState := req.URL.Query().Get("state"); true {
// log.Debug(resState)
// ctx := c.Context()
// _, err := memcache.Get(ctx, resState)
// if err != nil {
// log.Panic("CSRF attempt \n\tExpected: %s", resState)
// }
// }
// code := req.URL.Query().Get("code")
// if code == "" {
// log.Panic("No code found")
// }
// accessToken, err := exchangeCode(c, code)
// if err != nil {
// log.Panic(err)
// }
// session := newSession(c, accessToken)
// if err := session.Validate(); err != nil {
// log.Panic("AccessToken is invalid %s", session.AccessToken())
// }
// me, err := session.Get("/me", nil)
// if err != nil {
// log.Panic("Error accessing Graph API with accessToken", err)
// }
// user := new(models.User)
// if err := me.Decode(&user.Facebook); err != nil {
// log.Debug("%v")
// }
// if user.Facebook.Verified {
// log.Debug("Verified")
// user.FirstName = user.Facebook.FirstName
// user.LastName = user.Facebook.LastName
// user.Email = user.Facebook.Email
// q := queries.New(c)
// if err := q.UpsertUser(user); err != nil {
// log.Debug("Failed to upsert user")
// return
// }
// if err := auth.Login(c, user.Email); err != nil {
// log.Debug("Failed to login")
// log.Debug("%#v", err)
// c.Redirect(302, config.UrlFor("store", "/login"))
// return
// }
// c.Redirect(302, config.UrlFor("store"))
// } else {
// auth.Logout(c)
// c.Redirect(302, config.UrlFor("store", "/login"))
// }
return nil
}
// Generates a token and inserts it into memcache
// The token has a TTL of 3 minutes
var letters = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ")
func CSRFToken(c *zip.Ctx) string {
size := 16
b := make([]rune, size)
for i := range b {
b[i] = letters[rand.Intn(len(letters))]
}
token := string(b)
item := &cache.Item{
Key: token,
Value: []byte(token),
Expiration: 3 * time.Minute,
}
ctx := c.Context()
cache.Set(ctx, item)
return url.QueryEscape(token)
}
// GET /auth/facebook
func LoginUser(c *zip.Ctx) error {
// state := CSRFToken(c)
// log.Debug(state)
// url := fmt.Sprintf(
// "https://www.facebook.com/dialog/oauth?client_id=%s&state=%s&redirect_uri=%s&response_type=%s&scope=%s",
// appId, state,
// redirectUri(c), "code",
// "email",
// )
// if auth.IsLoggedIn(c) {
// log.Debug("Logged in")
// user, _ := auth.GetUser(c)
// if user.Facebook.AccessToken != "" {
// session := newSession(c, user.Facebook.AccessToken)
// if err := session.Validate(); err == nil {
// // Token is still valid
// auth.Login(c, user.Email)
// c.Redirect(302, "/profile")
// return
// }
// }
// }
// log.Debug("Not logged in")
// c.Redirect(302, url)
return nil
}
func exchangeCode(c *zip.Ctx, code string) (token string, err error) {
endpoint := fmt.Sprintf(
"https://graph.facebook.com/oauth/access_token?client_id=%s&redirect_uri=%s&client_secret=%s&code=%s",
appId, redirectUri(c), appSecret, code,
)
log.Debug(endpoint)
client := &http.Client{Timeout: 55 * time.Second}
req, err := http.NewRequest("GET", endpoint, nil)
if err != nil {
log.Panic(err)
}
res, err := client.Do(req)
if err != nil {
log.Panic(err)
}
defer res.Body.Close()
b, err := ioutil.ReadAll(res.Body)
if err != nil {
log.Panic(err)
}
body := string(b)
values, err := url.ParseQuery(body)
if err != nil {
return token, err
}
token = values.Get("access_token")
if token == "" {
return token, errors.New(body)
}
return token, nil
}