main pico / pkg / db / postgres / invites.go
Antonio Mika  ·  2026-10-11
 1package postgres
 2
 3import (
 4	"fmt"
 5	"time"
 6
 7	"github.com/picosh/pico/pkg/db"
 8)
 9
10func (me *PsqlDB) FindWhoInvitedUser(toUserID string) (string, error) {
11	var inviterName string
12	query := `
13        SELECT app_users.name
14        FROM invites
15        LEFT JOIN app_users ON app_users.id = invites.from_user_id
16        WHERE to_user_id=$1
17    `
18	err := me.Db.Get(&inviterName, query, toUserID)
19	if err != nil {
20		return "", err
21	}
22
23	return inviterName, nil
24}
25
26func (me *PsqlDB) FindInvitesByUser(userID string) ([]*db.Invite, error) {
27	var invites []*db.Invite
28	query := `SELECT
29		invites.id, invites.from_user_id, invites.to_user_id, invites.created_at,
30		app_users.name as to_user_name
31	FROM invites
32	LEFT JOIN app_users ON app_users.id = invites.to_user_id
33	WHERE from_user_id=$1`
34	err := me.Db.Select(&invites, query, userID)
35	if err != nil {
36		return nil, err
37	}
38	return invites, nil
39}
40
41// inviteFeatures are the services an invite grants access to.
42var inviteFeatures = []string{"prose", "pgs", "pastes"}
43
44func (me *PsqlDB) hasActiveInvite(userID string) (bool, error) {
45	var invited bool
46	err := me.Db.Get(&invited, `SELECT EXISTS (SELECT 1 FROM invites WHERE to_user_id = $1)`, userID)
47	if err != nil || !invited {
48		return false, err
49	}
50	for _, name := range inviteFeatures {
51		if db.HasFeature(me, userID, name) {
52			return true, nil
53		}
54	}
55	return false, nil
56}
57
58func (me *PsqlDB) InviteUser(fromUserID string, toUserID string) error {
59	canInvite := db.HasFeature(me, fromUserID, "plus")
60	if !canInvite {
61		invited, err := me.hasActiveInvite(fromUserID)
62		if err != nil {
63			return err
64		}
65		canInvite = invited
66	}
67
68	if !canInvite {
69		return fmt.Errorf("must have valid pico+ membership or an active invite from someone")
70	}
71
72	_, err := me.Db.Exec(
73		`INSERT INTO invites (from_user_id, to_user_id) VALUES ($1, $2)`,
74		fromUserID, toUserID,
75	)
76	if err != nil {
77		return err
78	}
79
80	expiresAt := time.Now().Add(5 * 365 * 24 * time.Hour)
81	for _, name := range inviteFeatures {
82		if _, err := me.InsertFeature(toUserID, name, expiresAt); err != nil {
83			return err
84		}
85	}
86	return nil
87}