scan-limits-gating: paid-order gating + per-tag daily cap + per-IP hourly rate (6/6 scenarios) — no gateway

This commit is contained in:
2026-08-08 09:25:06 +10:00
parent 92ccf3d0f3
commit 3d242fdcaa
7 changed files with 157 additions and 28 deletions

View File

@@ -35,6 +35,7 @@ type Scan struct {
ScannerPhone pgtype.Text `json:"scanner_phone"`
AlertSent bool `json:"alert_sent"`
Fingerprint pgtype.Text `json:"fingerprint"`
Ip pgtype.Text `json:"ip"`
}
type Tag struct {
@@ -56,10 +57,12 @@ type Tag struct {
}
type User struct {
ID int64 `json:"id"`
Email string `json:"email"`
PasswordHash string `json:"password_hash"`
Name pgtype.Text `json:"name"`
Phone pgtype.Text `json:"phone"`
CreatedAt pgtype.Timestamptz `json:"created_at"`
ID int64 `json:"id"`
Email string `json:"email"`
PasswordHash string `json:"password_hash"`
Name pgtype.Text `json:"name"`
Phone pgtype.Text `json:"phone"`
CreatedAt pgtype.Timestamptz `json:"created_at"`
IsAdmin bool `json:"is_admin"`
RememberToken pgtype.Text `json:"remember_token"`
}

View File

@@ -13,10 +13,13 @@ import (
type Querier interface {
BindTag(ctx context.Context, arg BindTagParams) (Tag, error)
ClearTagOwner(ctx context.Context, id int64) (Tag, error)
CountAlertsByIPSince(ctx context.Context, arg CountAlertsByIPSinceParams) (int64, error)
CountAlertsByTagSince(ctx context.Context, arg CountAlertsByTagSinceParams) (int64, error)
CountTagsByOwner(ctx context.Context, ownerID pgtype.Int8) (int64, error)
CreateUser(ctx context.Context, arg CreateUserParams) (User, error)
GetLastAlertByTag(ctx context.Context, tagID int64) (Scan, error)
GetLatestScanByTag(ctx context.Context, tagID int64) (Scan, error)
GetOrderByID(ctx context.Context, id int64) (Order, error)
GetRecentScanByFingerprint(ctx context.Context, arg GetRecentScanByFingerprintParams) (Scan, error)
GetTagByCode(ctx context.Context, tagCode string) (Tag, error)
GetTagByID(ctx context.Context, id int64) (Tag, error)

View File

@@ -47,8 +47,8 @@ INSERT INTO tags (tag_code) VALUES ($1)
RETURNING *;
-- name: InsertScan :one
INSERT INTO scans (tag_id, lat, lng, location_shared, scanner_phone, fingerprint)
VALUES ($1, $2, $3, $4, $5, $6)
INSERT INTO scans (tag_id, lat, lng, location_shared, scanner_phone, fingerprint, ip)
VALUES ($1, $2, $3, $4, $5, $6, $7)
RETURNING *;
-- name: GetLastAlertByTag :one
@@ -74,3 +74,14 @@ SELECT * FROM scans
WHERE fingerprint = $1 AND id <> $2 AND scanned_at > now() - interval '24 hours'
ORDER BY scanned_at DESC
LIMIT 1;
-- name: GetOrderByID :one
SELECT * FROM orders WHERE id = $1;
-- name: CountAlertsByTagSince :one
SELECT count(*) FROM scans
WHERE tag_id = $1 AND alert_sent = TRUE AND scanned_at > $2;
-- name: CountAlertsByIPSince :one
SELECT count(*) FROM scans
WHERE ip = $1 AND alert_sent = TRUE AND scanned_at > $2;

View File

@@ -76,6 +76,40 @@ func (q *Queries) ClearTagOwner(ctx context.Context, id int64) (Tag, error) {
return i, err
}
const countAlertsByIPSince = `-- name: CountAlertsByIPSince :one
SELECT count(*) FROM scans
WHERE ip = $1 AND alert_sent = TRUE AND scanned_at > $2
`
type CountAlertsByIPSinceParams struct {
Ip pgtype.Text `json:"ip"`
ScannedAt pgtype.Timestamptz `json:"scanned_at"`
}
func (q *Queries) CountAlertsByIPSince(ctx context.Context, arg CountAlertsByIPSinceParams) (int64, error) {
row := q.db.QueryRow(ctx, countAlertsByIPSince, arg.Ip, arg.ScannedAt)
var count int64
err := row.Scan(&count)
return count, err
}
const countAlertsByTagSince = `-- name: CountAlertsByTagSince :one
SELECT count(*) FROM scans
WHERE tag_id = $1 AND alert_sent = TRUE AND scanned_at > $2
`
type CountAlertsByTagSinceParams struct {
TagID int64 `json:"tag_id"`
ScannedAt pgtype.Timestamptz `json:"scanned_at"`
}
func (q *Queries) CountAlertsByTagSince(ctx context.Context, arg CountAlertsByTagSinceParams) (int64, error) {
row := q.db.QueryRow(ctx, countAlertsByTagSince, arg.TagID, arg.ScannedAt)
var count int64
err := row.Scan(&count)
return count, err
}
const countTagsByOwner = `-- name: CountTagsByOwner :one
SELECT count(*) FROM tags WHERE owner_id = $1
`
@@ -90,7 +124,7 @@ func (q *Queries) CountTagsByOwner(ctx context.Context, ownerID pgtype.Int8) (in
const createUser = `-- name: CreateUser :one
INSERT INTO users (email, password_hash, name, phone)
VALUES ($1, $2, $3, $4)
RETURNING id, email, password_hash, name, phone, created_at
RETURNING id, email, password_hash, name, phone, created_at, is_admin, remember_token
`
type CreateUserParams struct {
@@ -115,12 +149,14 @@ func (q *Queries) CreateUser(ctx context.Context, arg CreateUserParams) (User, e
&i.Name,
&i.Phone,
&i.CreatedAt,
&i.IsAdmin,
&i.RememberToken,
)
return i, err
}
const getLastAlertByTag = `-- name: GetLastAlertByTag :one
SELECT id, tag_id, scanned_at, lat, lng, location_shared, scanner_phone, alert_sent, fingerprint FROM scans
SELECT id, tag_id, scanned_at, lat, lng, location_shared, scanner_phone, alert_sent, fingerprint, ip FROM scans
WHERE tag_id = $1 AND alert_sent = TRUE
ORDER BY scanned_at DESC
LIMIT 1
@@ -139,12 +175,13 @@ func (q *Queries) GetLastAlertByTag(ctx context.Context, tagID int64) (Scan, err
&i.ScannerPhone,
&i.AlertSent,
&i.Fingerprint,
&i.Ip,
)
return i, err
}
const getLatestScanByTag = `-- name: GetLatestScanByTag :one
SELECT id, tag_id, scanned_at, lat, lng, location_shared, scanner_phone, alert_sent, fingerprint FROM scans
SELECT id, tag_id, scanned_at, lat, lng, location_shared, scanner_phone, alert_sent, fingerprint, ip FROM scans
WHERE tag_id = $1
ORDER BY scanned_at DESC
LIMIT 1
@@ -163,12 +200,30 @@ func (q *Queries) GetLatestScanByTag(ctx context.Context, tagID int64) (Scan, er
&i.ScannerPhone,
&i.AlertSent,
&i.Fingerprint,
&i.Ip,
)
return i, err
}
const getOrderByID = `-- name: GetOrderByID :one
SELECT id, account_id, status, created_at, updated_at FROM orders WHERE id = $1
`
func (q *Queries) GetOrderByID(ctx context.Context, id int64) (Order, error) {
row := q.db.QueryRow(ctx, getOrderByID, id)
var i Order
err := row.Scan(
&i.ID,
&i.AccountID,
&i.Status,
&i.CreatedAt,
&i.UpdatedAt,
)
return i, err
}
const getRecentScanByFingerprint = `-- name: GetRecentScanByFingerprint :one
SELECT id, tag_id, scanned_at, lat, lng, location_shared, scanner_phone, alert_sent, fingerprint FROM scans
SELECT id, tag_id, scanned_at, lat, lng, location_shared, scanner_phone, alert_sent, fingerprint, ip FROM scans
WHERE fingerprint = $1 AND id <> $2 AND scanned_at > now() - interval '24 hours'
ORDER BY scanned_at DESC
LIMIT 1
@@ -192,6 +247,7 @@ func (q *Queries) GetRecentScanByFingerprint(ctx context.Context, arg GetRecentS
&i.ScannerPhone,
&i.AlertSent,
&i.Fingerprint,
&i.Ip,
)
return i, err
}
@@ -251,7 +307,7 @@ func (q *Queries) GetTagByID(ctx context.Context, id int64) (Tag, error) {
}
const getUserByEmail = `-- name: GetUserByEmail :one
SELECT id, email, password_hash, name, phone, created_at FROM users WHERE email = $1
SELECT id, email, password_hash, name, phone, created_at, is_admin, remember_token FROM users WHERE email = $1
`
func (q *Queries) GetUserByEmail(ctx context.Context, email string) (User, error) {
@@ -264,12 +320,14 @@ func (q *Queries) GetUserByEmail(ctx context.Context, email string) (User, error
&i.Name,
&i.Phone,
&i.CreatedAt,
&i.IsAdmin,
&i.RememberToken,
)
return i, err
}
const getUserByID = `-- name: GetUserByID :one
SELECT id, email, password_hash, name, phone, created_at FROM users WHERE id = $1
SELECT id, email, password_hash, name, phone, created_at, is_admin, remember_token FROM users WHERE id = $1
`
func (q *Queries) GetUserByID(ctx context.Context, id int64) (User, error) {
@@ -282,14 +340,16 @@ func (q *Queries) GetUserByID(ctx context.Context, id int64) (User, error) {
&i.Name,
&i.Phone,
&i.CreatedAt,
&i.IsAdmin,
&i.RememberToken,
)
return i, err
}
const insertScan = `-- name: InsertScan :one
INSERT INTO scans (tag_id, lat, lng, location_shared, scanner_phone, fingerprint)
VALUES ($1, $2, $3, $4, $5, $6)
RETURNING id, tag_id, scanned_at, lat, lng, location_shared, scanner_phone, alert_sent, fingerprint
INSERT INTO scans (tag_id, lat, lng, location_shared, scanner_phone, fingerprint, ip)
VALUES ($1, $2, $3, $4, $5, $6, $7)
RETURNING id, tag_id, scanned_at, lat, lng, location_shared, scanner_phone, alert_sent, fingerprint, ip
`
type InsertScanParams struct {
@@ -299,6 +359,7 @@ type InsertScanParams struct {
LocationShared bool `json:"location_shared"`
ScannerPhone pgtype.Text `json:"scanner_phone"`
Fingerprint pgtype.Text `json:"fingerprint"`
Ip pgtype.Text `json:"ip"`
}
func (q *Queries) InsertScan(ctx context.Context, arg InsertScanParams) (Scan, error) {
@@ -309,6 +370,7 @@ func (q *Queries) InsertScan(ctx context.Context, arg InsertScanParams) (Scan, e
arg.LocationShared,
arg.ScannerPhone,
arg.Fingerprint,
arg.Ip,
)
var i Scan
err := row.Scan(
@@ -321,6 +383,7 @@ func (q *Queries) InsertScan(ctx context.Context, arg InsertScanParams) (Scan, e
&i.ScannerPhone,
&i.AlertSent,
&i.Fingerprint,
&i.Ip,
)
return i, err
}