feat: replace UUID v4 with UUID v7 across backend
Define uuid_generate_v7() in the first migration and switch all table primary key defaults to it. Remove the uuid-ossp extension dependency. Update refresh token and request ID generation in Go code to use uuid.NewV7() from the existing google/uuid v1.6.0 library. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -42,7 +42,7 @@ func (j *JWTManager) GenerateAccessToken(userID, plan string) (string, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (j *JWTManager) GenerateRefreshToken() (string, time.Time) {
|
func (j *JWTManager) GenerateRefreshToken() (string, time.Time) {
|
||||||
token := uuid.NewString()
|
token := uuid.Must(uuid.NewV7()).String()
|
||||||
expiresAt := time.Now().Add(j.refreshDuration)
|
expiresAt := time.Now().Add(j.refreshDuration)
|
||||||
return token, expiresAt
|
return token, expiresAt
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ func RequestID(next http.Handler) http.Handler {
|
|||||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
id := r.Header.Get("X-Request-ID")
|
id := r.Header.Get("X-Request-ID")
|
||||||
if id == "" {
|
if id == "" {
|
||||||
id = uuid.NewString()
|
id = uuid.Must(uuid.NewV7()).String()
|
||||||
}
|
}
|
||||||
ctx := context.WithValue(r.Context(), requestIDKey, id)
|
ctx := context.WithValue(r.Context(), requestIDKey, id)
|
||||||
w.Header().Set("X-Request-ID", id)
|
w.Header().Set("X-Request-ID", id)
|
||||||
|
|||||||
@@ -1,5 +1,35 @@
|
|||||||
-- +goose Up
|
-- +goose Up
|
||||||
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
|
|
||||||
|
-- Generate UUID v7 (time-ordered, millisecond precision).
|
||||||
|
-- Structure: 48-bit unix_ts_ms | 4-bit version (0111) | 12-bit rand_a | 2-bit variant (10) | 62-bit rand_b
|
||||||
|
CREATE OR REPLACE FUNCTION uuid_generate_v7()
|
||||||
|
RETURNS uuid
|
||||||
|
AS $$
|
||||||
|
DECLARE
|
||||||
|
unix_ts_ms bytea;
|
||||||
|
uuid_bytes bytea;
|
||||||
|
BEGIN
|
||||||
|
-- 48-bit Unix timestamp in milliseconds.
|
||||||
|
-- int8send produces 8 big-endian bytes; skip the first 2 zero bytes to get 6.
|
||||||
|
unix_ts_ms = substring(int8send(floor(extract(epoch from clock_timestamp()) * 1000)::bigint) from 3);
|
||||||
|
|
||||||
|
-- Use a random v4 UUID as the source of random bits for rand_a and rand_b.
|
||||||
|
uuid_bytes = uuid_send(gen_random_uuid());
|
||||||
|
|
||||||
|
-- Overwrite bytes 0–5 with the timestamp (positions 1–6 in 1-indexed bytea).
|
||||||
|
uuid_bytes = overlay(uuid_bytes placing unix_ts_ms from 1 for 6);
|
||||||
|
|
||||||
|
-- Set version nibble (bits 48–51) to 0111 (7).
|
||||||
|
uuid_bytes = set_bit(uuid_bytes, 48, 0);
|
||||||
|
uuid_bytes = set_bit(uuid_bytes, 49, 1);
|
||||||
|
uuid_bytes = set_bit(uuid_bytes, 50, 1);
|
||||||
|
uuid_bytes = set_bit(uuid_bytes, 51, 1);
|
||||||
|
|
||||||
|
-- Variant bits (64–65) stay at 10 as inherited from gen_random_uuid().
|
||||||
|
|
||||||
|
RETURN encode(uuid_bytes, 'hex')::uuid;
|
||||||
|
END
|
||||||
|
$$ LANGUAGE plpgsql VOLATILE;
|
||||||
|
|
||||||
CREATE TYPE user_plan AS ENUM ('free', 'paid');
|
CREATE TYPE user_plan AS ENUM ('free', 'paid');
|
||||||
CREATE TYPE user_gender AS ENUM ('male', 'female');
|
CREATE TYPE user_gender AS ENUM ('male', 'female');
|
||||||
@@ -7,7 +37,7 @@ CREATE TYPE user_goal AS ENUM ('lose', 'maintain', 'gain');
|
|||||||
CREATE TYPE activity_level AS ENUM ('low', 'moderate', 'high');
|
CREATE TYPE activity_level AS ENUM ('low', 'moderate', 'high');
|
||||||
|
|
||||||
CREATE TABLE users (
|
CREATE TABLE users (
|
||||||
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
id UUID PRIMARY KEY DEFAULT uuid_generate_v7(),
|
||||||
firebase_uid VARCHAR(128) NOT NULL UNIQUE,
|
firebase_uid VARCHAR(128) NOT NULL UNIQUE,
|
||||||
email VARCHAR(255) NOT NULL,
|
email VARCHAR(255) NOT NULL,
|
||||||
name VARCHAR(255) NOT NULL DEFAULT '',
|
name VARCHAR(255) NOT NULL DEFAULT '',
|
||||||
@@ -47,3 +77,4 @@ DROP TYPE IF EXISTS activity_level;
|
|||||||
DROP TYPE IF EXISTS user_goal;
|
DROP TYPE IF EXISTS user_goal;
|
||||||
DROP TYPE IF EXISTS user_gender;
|
DROP TYPE IF EXISTS user_gender;
|
||||||
DROP TYPE IF EXISTS user_plan;
|
DROP TYPE IF EXISTS user_plan;
|
||||||
|
DROP FUNCTION IF EXISTS uuid_generate_v7();
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
-- +goose Up
|
-- +goose Up
|
||||||
CREATE TABLE ingredient_mappings (
|
CREATE TABLE ingredient_mappings (
|
||||||
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
id UUID PRIMARY KEY DEFAULT uuid_generate_v7(),
|
||||||
canonical_name VARCHAR(255) NOT NULL,
|
canonical_name VARCHAR(255) NOT NULL,
|
||||||
canonical_name_ru VARCHAR(255),
|
canonical_name_ru VARCHAR(255),
|
||||||
spoonacular_id INTEGER UNIQUE,
|
spoonacular_id INTEGER UNIQUE,
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ CREATE TYPE recipe_source AS ENUM ('spoonacular', 'ai', 'user');
|
|||||||
CREATE TYPE recipe_difficulty AS ENUM ('easy', 'medium', 'hard');
|
CREATE TYPE recipe_difficulty AS ENUM ('easy', 'medium', 'hard');
|
||||||
|
|
||||||
CREATE TABLE recipes (
|
CREATE TABLE recipes (
|
||||||
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
id UUID PRIMARY KEY DEFAULT uuid_generate_v7(),
|
||||||
source recipe_source NOT NULL DEFAULT 'spoonacular',
|
source recipe_source NOT NULL DEFAULT 'spoonacular',
|
||||||
spoonacular_id INTEGER UNIQUE,
|
spoonacular_id INTEGER UNIQUE,
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
-- +goose Up
|
-- +goose Up
|
||||||
CREATE TABLE saved_recipes (
|
CREATE TABLE saved_recipes (
|
||||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
id UUID PRIMARY KEY DEFAULT uuid_generate_v7(),
|
||||||
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
||||||
title TEXT NOT NULL,
|
title TEXT NOT NULL,
|
||||||
description TEXT,
|
description TEXT,
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
-- +goose Up
|
-- +goose Up
|
||||||
CREATE TABLE products (
|
CREATE TABLE products (
|
||||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
id UUID PRIMARY KEY DEFAULT uuid_generate_v7(),
|
||||||
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
||||||
mapping_id UUID REFERENCES ingredient_mappings(id),
|
mapping_id UUID REFERENCES ingredient_mappings(id),
|
||||||
name TEXT NOT NULL,
|
name TEXT NOT NULL,
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
-- +goose Up
|
-- +goose Up
|
||||||
|
|
||||||
CREATE TABLE menu_plans (
|
CREATE TABLE menu_plans (
|
||||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
id UUID PRIMARY KEY DEFAULT uuid_generate_v7(),
|
||||||
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
||||||
week_start DATE NOT NULL,
|
week_start DATE NOT NULL,
|
||||||
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
||||||
@@ -9,7 +9,7 @@ CREATE TABLE menu_plans (
|
|||||||
);
|
);
|
||||||
|
|
||||||
CREATE TABLE menu_items (
|
CREATE TABLE menu_items (
|
||||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
id UUID PRIMARY KEY DEFAULT uuid_generate_v7(),
|
||||||
menu_plan_id UUID NOT NULL REFERENCES menu_plans(id) ON DELETE CASCADE,
|
menu_plan_id UUID NOT NULL REFERENCES menu_plans(id) ON DELETE CASCADE,
|
||||||
day_of_week INT NOT NULL CHECK (day_of_week BETWEEN 1 AND 7),
|
day_of_week INT NOT NULL CHECK (day_of_week BETWEEN 1 AND 7),
|
||||||
meal_type TEXT NOT NULL CHECK (meal_type IN ('breakfast','lunch','dinner')),
|
meal_type TEXT NOT NULL CHECK (meal_type IN ('breakfast','lunch','dinner')),
|
||||||
@@ -21,7 +21,7 @@ CREATE TABLE menu_items (
|
|||||||
-- Stores the generated shopping list for a menu plan.
|
-- Stores the generated shopping list for a menu plan.
|
||||||
-- items is a JSONB array of {name, category, amount, unit, checked, in_stock}.
|
-- items is a JSONB array of {name, category, amount, unit, checked, in_stock}.
|
||||||
CREATE TABLE shopping_lists (
|
CREATE TABLE shopping_lists (
|
||||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
id UUID PRIMARY KEY DEFAULT uuid_generate_v7(),
|
||||||
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
||||||
menu_plan_id UUID REFERENCES menu_plans(id) ON DELETE CASCADE,
|
menu_plan_id UUID REFERENCES menu_plans(id) ON DELETE CASCADE,
|
||||||
items JSONB NOT NULL DEFAULT '[]',
|
items JSONB NOT NULL DEFAULT '[]',
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
-- +goose Up
|
-- +goose Up
|
||||||
|
|
||||||
CREATE TABLE meal_diary (
|
CREATE TABLE meal_diary (
|
||||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
id UUID PRIMARY KEY DEFAULT uuid_generate_v7(),
|
||||||
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
||||||
date DATE NOT NULL,
|
date DATE NOT NULL,
|
||||||
meal_type TEXT NOT NULL,
|
meal_type TEXT NOT NULL,
|
||||||
|
|||||||
Reference in New Issue
Block a user