2022-04-16 19:42:09 +00:00
package main
import (
"errors"
"net/http"
2022-04-17 05:27:17 +00:00
"github.com/dgraph-io/ristretto"
2022-04-16 19:42:09 +00:00
"github.com/samber/lo"
2023-01-24 15:49:33 +00:00
"go.goblog.app/app/pkgs/builderpool"
2022-04-16 19:42:09 +00:00
)
// Hardcoded for now
var allowedReactions = [ ] string {
"❤️" ,
"👍" ,
"🎉" ,
"😂" ,
"😱" ,
}
func ( a * goBlog ) reactionsEnabled ( ) bool {
return a . cfg . Reactions != nil && a . cfg . Reactions . Enabled
}
const reactionsPostParam = "reactions"
func ( a * goBlog ) reactionsEnabledForPost ( post * post ) bool {
return a . reactionsEnabled ( ) && post != nil && post . firstParameter ( reactionsPostParam ) != "false"
}
2022-04-17 05:27:17 +00:00
func ( a * goBlog ) initReactions ( ) {
a . reactionsInit . Do ( func ( ) {
2022-04-17 05:40:02 +00:00
if ! a . reactionsEnabled ( ) {
return
}
2022-04-17 05:27:17 +00:00
a . reactionsCache , _ = ristretto . NewCache ( & ristretto . Config {
NumCounters : 1000 ,
MaxCost : 100 , // Cache reactions for 100 posts
BufferItems : 64 ,
IgnoreInternalCost : true ,
} )
} )
}
2022-04-17 05:40:02 +00:00
func ( a * goBlog ) deleteReactionsCache ( path string ) {
a . initReactions ( )
if a . reactionsCache != nil {
a . reactionsCache . Del ( path )
}
}
2022-04-16 19:42:09 +00:00
func ( a * goBlog ) postReaction ( w http . ResponseWriter , r * http . Request ) {
path := r . FormValue ( "path" )
reaction := r . FormValue ( "reaction" )
if path == "" || reaction == "" {
a . serveError ( w , r , "" , http . StatusBadRequest )
return
}
err := a . saveReaction ( reaction , path )
if err != nil {
a . serveError ( w , r , "" , http . StatusBadRequest )
return
}
}
func ( a * goBlog ) saveReaction ( reaction , path string ) error {
// Check if reaction is allowed
if ! lo . Contains ( allowedReactions , reaction ) {
return errors . New ( "reaction not allowed" )
}
2022-04-17 05:27:17 +00:00
// Init
a . initReactions ( )
// Delete from cache
defer a . reactionsSfg . Forget ( path )
defer a . reactionsCache . Del ( path )
2022-04-16 19:42:09 +00:00
// Insert reaction
2022-08-09 15:25:22 +00:00
_ , err := a . db . Exec ( "insert into reactions (path, reaction, count) values (?, ?, 1) on conflict (path, reaction) do update set count=count+1" , path , reaction )
2022-04-16 19:42:09 +00:00
return err
}
func ( a * goBlog ) getReactions ( w http . ResponseWriter , r * http . Request ) {
path := r . FormValue ( "path" )
reactions , err := a . getReactionsFromDatabase ( path )
if err != nil {
a . serveError ( w , r , "" , http . StatusInternalServerError )
return
}
2023-04-11 14:56:40 +00:00
w . Header ( ) . Set ( cacheControl , "no-store" )
2023-11-11 18:13:50 +00:00
a . respondWithMinifiedJson ( w , reactions )
2022-04-16 19:42:09 +00:00
}
func ( a * goBlog ) getReactionsFromDatabase ( path string ) ( map [ string ] int , error ) {
2022-04-17 05:27:17 +00:00
// Init
a . initReactions ( )
// Check cache
if val , cached := a . reactionsCache . Get ( path ) ; cached {
// Return from cache
return val . ( map [ string ] int ) , nil
2022-04-16 19:42:09 +00:00
}
2022-04-17 05:27:17 +00:00
// Get reactions
2022-06-07 19:55:45 +00:00
res , err , _ := a . reactionsSfg . Do ( path , func ( ) ( any , error ) {
2022-04-17 05:27:17 +00:00
// Build query
2023-01-24 15:49:33 +00:00
sqlBuf := builderpool . Get ( )
defer builderpool . Put ( sqlBuf )
2022-04-17 05:27:17 +00:00
sqlArgs := [ ] any { }
sqlBuf . WriteString ( "select reaction, count from reactions where path=? and reaction in (" )
sqlArgs = append ( sqlArgs , path )
for i , reaction := range allowedReactions {
if i > 0 {
sqlBuf . WriteString ( "," )
}
sqlBuf . WriteString ( "?" )
sqlArgs = append ( sqlArgs , reaction )
}
sqlBuf . WriteString ( ") and path not in (select path from post_parameters where parameter=? and value=?)" )
sqlArgs = append ( sqlArgs , reactionsPostParam , "false" )
// Execute query
2022-08-09 15:25:22 +00:00
rows , err := a . db . Query ( sqlBuf . String ( ) , sqlArgs ... )
2022-04-16 19:42:09 +00:00
if err != nil {
return nil , err
}
2022-04-17 05:27:17 +00:00
// Build result
defer rows . Close ( )
reactions := map [ string ] int { }
for rows . Next ( ) {
var reaction string
var count int
err = rows . Scan ( & reaction , & count )
if err != nil {
return nil , err
}
reactions [ reaction ] = count
}
// Cache result
a . reactionsCache . Set ( path , reactions , 1 )
return reactions , nil
} )
if err != nil || res == nil {
return nil , err
2022-04-16 19:42:09 +00:00
}
2022-04-17 05:27:17 +00:00
return res . ( map [ string ] int ) , nil
2022-04-16 19:42:09 +00:00
}