Skip to content

Commit

Permalink
use UUIDv7 to generate post IDs
Browse files Browse the repository at this point in the history
  • Loading branch information
dimkr committed Jan 13, 2025
1 parent 0bab634 commit 0ac1e51
Show file tree
Hide file tree
Showing 13 changed files with 64 additions and 27 deletions.
4 changes: 4 additions & 0 deletions cmd/tootik/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ import (
"net/http"
"os"

"github.com/google/uuid"

"os/signal"
"sync"
"syscall"
Expand Down Expand Up @@ -103,6 +105,8 @@ func main() {
flag.Usage()
}

uuid.EnableRandPool()

var cfg cfg.Config

if *dumpCfg {
Expand Down
6 changes: 5 additions & 1 deletion fed/followers.go
Original file line number Diff line number Diff line change
Expand Up @@ -299,7 +299,11 @@ func (d *followersDigest) Sync(ctx context.Context, domain string, cfg *cfg.Conf

var followID string
if err := db.QueryRowContext(ctx, `SELECT id FROM follows WHERE follower = ? AND followed = ?`, follower, d.Followed).Scan(&followID); err != nil && errors.Is(err, sql.ErrNoRows) {
followID = fmt.Sprintf("https://%s/follow/%x", domain, sha256.Sum256([]byte(fmt.Sprintf("%s|%s|%d", follower, d.Followed, time.Now().UnixNano()))))
followID, err = outbox.NewID(domain, "follow")
if err != nil {
slog.Warn("Failed to generate fake follow ID", "followed", d.Followed, "follower", follower, "error", err)
continue
}
slog.Warn("Using fake follow ID to remove unknown remote follow", "followed", d.Followed, "follower", follower, "id", followID)
} else if err != nil {
slog.Warn("Failed to fetch follow ID of unknown remote follow", "followed", d.Followed, "follower", follower, "error", err)
Expand Down
9 changes: 7 additions & 2 deletions front/post.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ limitations under the License.
package front

import (
"crypto/sha256"
"database/sql"
"errors"
"fmt"
Expand Down Expand Up @@ -84,7 +83,13 @@ func (h *Handler) post(w text.Writer, r *Request, oldNote *ap.Object, inReplyTo

var postID string
if oldNote == nil {
postID = fmt.Sprintf("https://%s/post/%x", h.Domain, sha256.Sum256([]byte(fmt.Sprintf("%s|%s|%d", r.User.ID, content, now.Unix()))))
var err error
postID, err = outbox.NewID(h.Domain, "post")
if err != nil {
r.Log.Error("Failed to generate post ID", "error", err)
w.Error()
return
}
} else {
postID = oldNote.ID
}
Expand Down
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ go 1.23.4

require (
github.com/fsnotify/fsnotify v1.8.0
github.com/google/uuid v1.6.0
github.com/mattn/go-sqlite3 v1.14.24
github.com/stretchr/testify v1.10.0
golang.org/x/image v0.23.0
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/fsnotify/fsnotify v1.8.0 h1:dAwr6QBTBZIkG8roQaJjGof0pp0EeF+tNV7YBP3F/8M=
github.com/fsnotify/fsnotify v1.8.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/mattn/go-sqlite3 v1.14.24 h1:tpSp2G2KyMnnQu99ngJ47EIkWVmliIizyZBfPrBWDRM=
github.com/mattn/go-sqlite3 v1.14.24/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
Expand Down
9 changes: 6 additions & 3 deletions outbox/accept.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,16 +18,19 @@ package outbox

import (
"context"
"crypto/sha256"
"database/sql"
"fmt"
"time"

"github.com/dimkr/tootik/ap"
)

// Accept queues an Accept activity for delivery.
func Accept(ctx context.Context, domain string, followed, follower, followID string, db *sql.DB) error {
id, err := NewID(domain, "accept")
if err != nil {
return err
}

recipients := ap.Audience{}
recipients.Add(follower)

Expand All @@ -40,7 +43,7 @@ func Accept(ctx context.Context, domain string, followed, follower, followID str
accept := ap.Activity{
Context: "https://www.w3.org/ns/activitystreams",
Type: ap.Accept,
ID: fmt.Sprintf("https://%s/accept/%x", domain, sha256.Sum256([]byte(fmt.Sprintf("%s|%s|%d", followed, follower, time.Now().UnixNano())))),
ID: id,
Actor: followed,
To: recipients,
Object: &ap.Activity{
Expand Down
6 changes: 4 additions & 2 deletions outbox/announce.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ package outbox

import (
"context"
"crypto/sha256"
"database/sql"
"fmt"
"time"
Expand All @@ -29,7 +28,10 @@ import (
// Announce queues an Announce activity for delivery.
func Announce(ctx context.Context, domain string, tx *sql.Tx, actor *ap.Actor, note *ap.Object) error {
now := time.Now()
announceID := fmt.Sprintf("https://%s/announce/%x", domain, sha256.Sum256([]byte(fmt.Sprintf("%s|%s|%d", actor.ID, note.ID, now.UnixNano()))))
announceID, err := NewID(domain, "announce")
if err != nil {
return err
}

to := ap.Audience{}
to.Add(ap.Public)
Expand Down
9 changes: 6 additions & 3 deletions outbox/create.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,10 @@ package outbox

import (
"context"
"crypto/sha256"
"database/sql"
"encoding/json"
"errors"
"fmt"
"time"

"github.com/dimkr/tootik/ap"
"github.com/dimkr/tootik/cfg"
Expand All @@ -36,6 +34,11 @@ var ErrDeliveryQueueFull = errors.New("delivery queue is full")

// Create queues a Create activity for delivery.
func Create(ctx context.Context, domain string, cfg *cfg.Config, db *sql.DB, post *ap.Object, author *ap.Actor) error {
id, err := NewID(domain, "create")
if err != nil {
return err
}

var queueSize int
if err := db.QueryRowContext(ctx, `select count(distinct activity->'$.id') from outbox where sent = 0 and attempts < ?`, cfg.MaxDeliveryAttempts).Scan(&queueSize); err != nil {
return fmt.Errorf("failed to query delivery queue size: %w", err)
Expand All @@ -48,7 +51,7 @@ func Create(ctx context.Context, domain string, cfg *cfg.Config, db *sql.DB, pos
create := ap.Activity{
Context: "https://www.w3.org/ns/activitystreams",
Type: ap.Create,
ID: fmt.Sprintf("https://%s/create/%x", domain, sha256.Sum256([]byte(fmt.Sprintf("%s|%d", post.ID, time.Now().Unix())))),
ID: id,
Actor: author.ID,
Object: post,
To: post.To,
Expand Down
7 changes: 4 additions & 3 deletions outbox/follow.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,9 @@ package outbox

import (
"context"
"crypto/sha256"
"database/sql"
"fmt"
"strings"
"time"

"github.com/dimkr/tootik/ap"
)
Expand All @@ -33,7 +31,10 @@ func Follow(ctx context.Context, domain string, follower *ap.Actor, followed str
return fmt.Errorf("%s cannot follow %s", follower.ID, followed)
}

followID := fmt.Sprintf("https://%s/follow/%x", domain, sha256.Sum256([]byte(fmt.Sprintf("%s|%s|%d", follower.ID, followed, time.Now().UnixNano()))))
followID, err := NewID(domain, "follow")
if err != nil {
return err
}

to := ap.Audience{}
to.Add(followed)
Expand Down
8 changes: 6 additions & 2 deletions outbox/move.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ package outbox

import (
"context"
"crypto/sha256"
"database/sql"
"fmt"
"log/slog"
Expand Down Expand Up @@ -133,9 +132,14 @@ func Move(ctx context.Context, db *sql.DB, domain string, from *ap.Actor, to str
aud := ap.Audience{}
aud.Add(from.Followers)

id, err := NewID(domain, "move")
if err != nil {
return err
}

move := ap.Activity{
Context: "https://www.w3.org/ns/activitystreams",
ID: fmt.Sprintf("https://%s/move/%x", domain, sha256.Sum256([]byte(fmt.Sprintf("%s|%s|%d", from.ID, to, now.UnixNano())))),
ID: id,
Actor: from.ID,
Type: ap.Move,
Object: from.ID,
Expand Down
9 changes: 6 additions & 3 deletions outbox/undo.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,9 @@ package outbox

import (
"context"
"crypto/sha256"
"database/sql"
"errors"
"fmt"
"time"

"github.com/dimkr/tootik/ap"
)
Expand All @@ -34,12 +32,17 @@ func Undo(ctx context.Context, domain string, db *sql.DB, activity *ap.Activity)
return errors.New("cannot undo activity")
}

id, err := NewID(domain, "undo")
if err != nil {
return err
}

to := activity.To
to.Add(ap.Public)

undo := ap.Activity{
Context: "https://www.w3.org/ns/activitystreams",
ID: fmt.Sprintf("https://%s/undo/%x", domain, sha256.Sum256([]byte(fmt.Sprintf("%s|%d", activity.ID, time.Now().UnixNano())))),
ID: id,
Type: ap.Undo,
Actor: activity.Actor,
To: to,
Expand Down
9 changes: 5 additions & 4 deletions outbox/unfollow.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,22 +18,23 @@ package outbox

import (
"context"
"crypto/sha256"
"database/sql"
"errors"
"fmt"
"time"

"github.com/dimkr/tootik/ap"
)

// Unfollow queues an Unfollow activity for delivery.
// Unfollow queues an Undo activity for delivery.
func Unfollow(ctx context.Context, domain string, db *sql.DB, follower, followed, followID string) error {
if followed == follower {
return fmt.Errorf("%s cannot unfollow %s", follower, followed)
}

undoID := fmt.Sprintf("https://%s/undo/%x", domain, sha256.Sum256([]byte(fmt.Sprintf("%s|%s|%d", follower, followed, time.Now().UnixNano()))))
undoID, err := NewID(domain, "undo")
if err != nil {
return err
}

to := ap.Audience{}
to.Add(followed)
Expand Down
12 changes: 8 additions & 4 deletions outbox/update.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,9 @@ package outbox

import (
"context"
"crypto/sha256"
"database/sql"
"encoding/json"
"fmt"
"time"

"github.com/dimkr/tootik/ap"
"github.com/dimkr/tootik/cfg"
Expand All @@ -31,7 +29,10 @@ import (

// UpdateNote queues an Update activity for delivery.
func UpdateNote(ctx context.Context, domain string, cfg *cfg.Config, db *sql.DB, note *ap.Object) error {
updateID := fmt.Sprintf("https://%s/update/%x", domain, sha256.Sum256([]byte(fmt.Sprintf("%s|%d", note.ID, time.Now().UnixNano()))))
updateID, err := NewID(domain, "update")
if err != nil {
return err
}

update := ap.Activity{
Context: "https://www.w3.org/ns/activitystreams",
Expand Down Expand Up @@ -116,7 +117,10 @@ func UpdateNote(ctx context.Context, domain string, cfg *cfg.Config, db *sql.DB,

// UpdateActor queues an Update activity for delivery.
func UpdateActor(ctx context.Context, domain string, tx *sql.Tx, actorID string) error {
updateID := fmt.Sprintf("https://%s/update/%x", domain, sha256.Sum256([]byte(fmt.Sprintf("%s|%d", actorID, time.Now().UnixNano()))))
updateID, err := NewID(domain, "update")
if err != nil {
return err
}

to := ap.Audience{}
to.Add(ap.Public)
Expand Down

0 comments on commit 0ac1e51

Please sign in to comment.