Audit fixes: VIP self-grant, draft leak, onboarding flag, hero personalization, gated downloads
Deploy GitHub Pages / deploy (push) Canceled after 0s

This commit is contained in:
AHMED
2026-10-09 16:20:55 +03:00
parent c34611dc36
commit 5ee706ebd6
150 changed files with 24054 additions and 0 deletions
File diff suppressed because it is too large Load Diff
+169
View File
@@ -0,0 +1,169 @@
import { MigrateUpArgs, MigrateDownArgs, sql } from '@payloadcms/db-postgres'
export async function up({ db, payload, req }: MigrateUpArgs): Promise<void> {
await db.execute(sql`
CREATE TYPE "public"."enum_users_role" AS ENUM('free', 'vip', 'admin');
CREATE TYPE "public"."enum_posts_access" AS ENUM('free', 'vip');
CREATE TYPE "public"."enum_posts_category" AS ENUM('وكلاء', 'نماذج', 'استرجاع', 'سلامة', 'أدوات', 'أعمال');
CREATE TYPE "public"."enum_posts_status" AS ENUM('draft', 'published');
CREATE TABLE "users_sessions" (
"_order" integer NOT NULL,
"_parent_id" integer NOT NULL,
"id" varchar PRIMARY KEY NOT NULL,
"created_at" timestamp(3) with time zone,
"expires_at" timestamp(3) with time zone NOT NULL
);
CREATE TABLE "users" (
"id" serial PRIMARY KEY NOT NULL,
"role" "enum_users_role" DEFAULT 'free' NOT NULL,
"stripe_customer_id" varchar,
"updated_at" timestamp(3) with time zone DEFAULT now() NOT NULL,
"created_at" timestamp(3) with time zone DEFAULT now() NOT NULL,
"email" varchar NOT NULL,
"reset_password_token" varchar,
"reset_password_expiration" timestamp(3) with time zone,
"salt" varchar,
"hash" varchar,
"reset_password_requested_at" timestamp(3) with time zone,
"login_attempts" numeric DEFAULT 0,
"lock_until" timestamp(3) with time zone
);
CREATE TABLE "posts" (
"id" serial PRIMARY KEY NOT NULL,
"title" varchar NOT NULL,
"slug" varchar NOT NULL,
"dek" varchar NOT NULL,
"access" "enum_posts_access" DEFAULT 'free' NOT NULL,
"teaser" varchar,
"content" jsonb NOT NULL,
"category" "enum_posts_category" NOT NULL,
"cover_id" integer NOT NULL,
"reading_minutes" numeric DEFAULT 5 NOT NULL,
"published_at" timestamp(3) with time zone,
"status" "enum_posts_status" DEFAULT 'draft' NOT NULL,
"updated_at" timestamp(3) with time zone DEFAULT now() NOT NULL,
"created_at" timestamp(3) with time zone DEFAULT now() NOT NULL
);
CREATE TABLE "media" (
"id" serial PRIMARY KEY NOT NULL,
"updated_at" timestamp(3) with time zone DEFAULT now() NOT NULL,
"created_at" timestamp(3) with time zone DEFAULT now() NOT NULL,
"url" varchar,
"thumbnail_u_r_l" varchar,
"filename" varchar,
"mime_type" varchar,
"filesize" numeric,
"width" numeric,
"height" numeric,
"focal_x" numeric,
"focal_y" numeric
);
CREATE TABLE "payload_kv" (
"id" serial PRIMARY KEY NOT NULL,
"key" varchar NOT NULL,
"data" jsonb NOT NULL
);
CREATE TABLE "payload_locked_documents" (
"id" serial PRIMARY KEY NOT NULL,
"global_slug" varchar,
"updated_at" timestamp(3) with time zone DEFAULT now() NOT NULL,
"created_at" timestamp(3) with time zone DEFAULT now() NOT NULL
);
CREATE TABLE "payload_locked_documents_rels" (
"id" serial PRIMARY KEY NOT NULL,
"order" integer,
"parent_id" integer NOT NULL,
"path" varchar NOT NULL,
"users_id" integer,
"posts_id" integer,
"media_id" integer
);
CREATE TABLE "payload_preferences" (
"id" serial PRIMARY KEY NOT NULL,
"key" varchar,
"value" jsonb,
"updated_at" timestamp(3) with time zone DEFAULT now() NOT NULL,
"created_at" timestamp(3) with time zone DEFAULT now() NOT NULL
);
CREATE TABLE "payload_preferences_rels" (
"id" serial PRIMARY KEY NOT NULL,
"order" integer,
"parent_id" integer NOT NULL,
"path" varchar NOT NULL,
"users_id" integer
);
CREATE TABLE "payload_migrations" (
"id" serial PRIMARY KEY NOT NULL,
"name" varchar,
"batch" numeric,
"updated_at" timestamp(3) with time zone DEFAULT now() NOT NULL,
"created_at" timestamp(3) with time zone DEFAULT now() NOT NULL
);
ALTER TABLE "users_sessions" ADD CONSTRAINT "users_sessions_parent_id_fk" FOREIGN KEY ("_parent_id") REFERENCES "public"."users"("id") ON DELETE cascade ON UPDATE no action;
ALTER TABLE "posts" ADD CONSTRAINT "posts_cover_id_media_id_fk" FOREIGN KEY ("cover_id") REFERENCES "public"."media"("id") ON DELETE set null ON UPDATE no action;
ALTER TABLE "payload_locked_documents_rels" ADD CONSTRAINT "payload_locked_documents_rels_parent_fk" FOREIGN KEY ("parent_id") REFERENCES "public"."payload_locked_documents"("id") ON DELETE cascade ON UPDATE no action;
ALTER TABLE "payload_locked_documents_rels" ADD CONSTRAINT "payload_locked_documents_rels_users_fk" FOREIGN KEY ("users_id") REFERENCES "public"."users"("id") ON DELETE cascade ON UPDATE no action;
ALTER TABLE "payload_locked_documents_rels" ADD CONSTRAINT "payload_locked_documents_rels_posts_fk" FOREIGN KEY ("posts_id") REFERENCES "public"."posts"("id") ON DELETE cascade ON UPDATE no action;
ALTER TABLE "payload_locked_documents_rels" ADD CONSTRAINT "payload_locked_documents_rels_media_fk" FOREIGN KEY ("media_id") REFERENCES "public"."media"("id") ON DELETE cascade ON UPDATE no action;
ALTER TABLE "payload_preferences_rels" ADD CONSTRAINT "payload_preferences_rels_parent_fk" FOREIGN KEY ("parent_id") REFERENCES "public"."payload_preferences"("id") ON DELETE cascade ON UPDATE no action;
ALTER TABLE "payload_preferences_rels" ADD CONSTRAINT "payload_preferences_rels_users_fk" FOREIGN KEY ("users_id") REFERENCES "public"."users"("id") ON DELETE cascade ON UPDATE no action;
CREATE INDEX "users_sessions_order_idx" ON "users_sessions" USING btree ("_order");
CREATE INDEX "users_sessions_parent_id_idx" ON "users_sessions" USING btree ("_parent_id");
CREATE INDEX "users_updated_at_idx" ON "users" USING btree ("updated_at");
CREATE INDEX "users_created_at_idx" ON "users" USING btree ("created_at");
CREATE UNIQUE INDEX "users_email_idx" ON "users" USING btree ("email");
CREATE UNIQUE INDEX "posts_slug_idx" ON "posts" USING btree ("slug");
CREATE INDEX "posts_cover_idx" ON "posts" USING btree ("cover_id");
CREATE INDEX "posts_updated_at_idx" ON "posts" USING btree ("updated_at");
CREATE INDEX "posts_created_at_idx" ON "posts" USING btree ("created_at");
CREATE INDEX "media_updated_at_idx" ON "media" USING btree ("updated_at");
CREATE INDEX "media_created_at_idx" ON "media" USING btree ("created_at");
CREATE UNIQUE INDEX "media_filename_idx" ON "media" USING btree ("filename");
CREATE UNIQUE INDEX "payload_kv_key_idx" ON "payload_kv" USING btree ("key");
CREATE INDEX "payload_locked_documents_global_slug_idx" ON "payload_locked_documents" USING btree ("global_slug");
CREATE INDEX "payload_locked_documents_updated_at_idx" ON "payload_locked_documents" USING btree ("updated_at");
CREATE INDEX "payload_locked_documents_created_at_idx" ON "payload_locked_documents" USING btree ("created_at");
CREATE INDEX "payload_locked_documents_rels_order_idx" ON "payload_locked_documents_rels" USING btree ("order");
CREATE INDEX "payload_locked_documents_rels_parent_idx" ON "payload_locked_documents_rels" USING btree ("parent_id");
CREATE INDEX "payload_locked_documents_rels_path_idx" ON "payload_locked_documents_rels" USING btree ("path");
CREATE INDEX "payload_locked_documents_rels_users_id_idx" ON "payload_locked_documents_rels" USING btree ("users_id");
CREATE INDEX "payload_locked_documents_rels_posts_id_idx" ON "payload_locked_documents_rels" USING btree ("posts_id");
CREATE INDEX "payload_locked_documents_rels_media_id_idx" ON "payload_locked_documents_rels" USING btree ("media_id");
CREATE INDEX "payload_preferences_key_idx" ON "payload_preferences" USING btree ("key");
CREATE INDEX "payload_preferences_updated_at_idx" ON "payload_preferences" USING btree ("updated_at");
CREATE INDEX "payload_preferences_created_at_idx" ON "payload_preferences" USING btree ("created_at");
CREATE INDEX "payload_preferences_rels_order_idx" ON "payload_preferences_rels" USING btree ("order");
CREATE INDEX "payload_preferences_rels_parent_idx" ON "payload_preferences_rels" USING btree ("parent_id");
CREATE INDEX "payload_preferences_rels_path_idx" ON "payload_preferences_rels" USING btree ("path");
CREATE INDEX "payload_preferences_rels_users_id_idx" ON "payload_preferences_rels" USING btree ("users_id");
CREATE INDEX "payload_migrations_updated_at_idx" ON "payload_migrations" USING btree ("updated_at");
CREATE INDEX "payload_migrations_created_at_idx" ON "payload_migrations" USING btree ("created_at");`)
}
export async function down({ db, payload, req }: MigrateDownArgs): Promise<void> {
await db.execute(sql`
DROP TABLE "users_sessions" CASCADE;
DROP TABLE "users" CASCADE;
DROP TABLE "posts" CASCADE;
DROP TABLE "media" CASCADE;
DROP TABLE "payload_kv" CASCADE;
DROP TABLE "payload_locked_documents" CASCADE;
DROP TABLE "payload_locked_documents_rels" CASCADE;
DROP TABLE "payload_preferences" CASCADE;
DROP TABLE "payload_preferences_rels" CASCADE;
DROP TABLE "payload_migrations" CASCADE;
DROP TYPE "public"."enum_users_role";
DROP TYPE "public"."enum_posts_access";
DROP TYPE "public"."enum_posts_category";
DROP TYPE "public"."enum_posts_status";`)
}
@@ -0,0 +1,13 @@
import { MigrateUpArgs, MigrateDownArgs, sql } from '@payloadcms/db-postgres'
export async function up({ db }: MigrateUpArgs): Promise<void> {
await db.execute(sql`
ALTER TABLE "users" ADD COLUMN IF NOT EXISTS "full_name" text;
`)
}
export async function down({ db }: MigrateDownArgs): Promise<void> {
await db.execute(sql`
ALTER TABLE "users" DROP COLUMN IF EXISTS "full_name";
`)
}
File diff suppressed because it is too large Load Diff
+62
View File
@@ -0,0 +1,62 @@
import { MigrateUpArgs, MigrateDownArgs, sql } from '@payloadcms/db-postgres'
export async function up({ db, payload, req }: MigrateUpArgs): Promise<void> {
await db.execute(sql`
CREATE TYPE "public"."enum_users_interest" AS ENUM('الوكلاء', 'RAG', 'الأتمتة', 'النماذج المفتوحة');
CREATE TYPE "public"."enum_users_level" AS ENUM('مبتدئ', 'متوسط', 'متقدم');
CREATE TYPE "public"."enum_users_goal" AS ENUM('بناء مشروع ذكاء اصطناعي', 'تطوير مساري المهني', 'أتمتة العمل', 'فهم الوكلاء بعمق');
CREATE TYPE "public"."enum_posts_level" AS ENUM('مبتدئ', 'متوسط', 'متقدم');
CREATE TABLE "post_likes" (
"id" serial PRIMARY KEY NOT NULL,
"user_id" integer NOT NULL,
"post_id" integer NOT NULL,
"updated_at" timestamp(3) with time zone DEFAULT now() NOT NULL,
"created_at" timestamp(3) with time zone DEFAULT now() NOT NULL
);
ALTER TABLE "users" ADD COLUMN "full_name" varchar;
ALTER TABLE "users" ADD COLUMN "interest" "enum_users_interest";
ALTER TABLE "users" ADD COLUMN "level" "enum_users_level";
ALTER TABLE "users" ADD COLUMN "goal" "enum_users_goal";
ALTER TABLE "users" ADD COLUMN "prefs" jsonb;
ALTER TABLE "users" ADD COLUMN "email_verified" boolean DEFAULT false;
ALTER TABLE "users" ADD COLUMN "verification_token" varchar;
ALTER TABLE "users" ADD COLUMN "onboarded" boolean DEFAULT false;
ALTER TABLE "posts" ADD COLUMN "level" "enum_posts_level" DEFAULT 'مبتدئ' NOT NULL;
ALTER TABLE "posts" ADD COLUMN "views" numeric DEFAULT 0;
ALTER TABLE "posts" ADD COLUMN "likes" numeric DEFAULT 0;
ALTER TABLE "payload_locked_documents_rels" ADD COLUMN "post_likes_id" integer;
ALTER TABLE "post_likes" ADD CONSTRAINT "post_likes_user_id_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."users"("id") ON DELETE set null ON UPDATE no action;
ALTER TABLE "post_likes" ADD CONSTRAINT "post_likes_post_id_posts_id_fk" FOREIGN KEY ("post_id") REFERENCES "public"."posts"("id") ON DELETE set null ON UPDATE no action;
CREATE INDEX "post_likes_user_idx" ON "post_likes" USING btree ("user_id");
CREATE INDEX "post_likes_post_idx" ON "post_likes" USING btree ("post_id");
CREATE INDEX "post_likes_updated_at_idx" ON "post_likes" USING btree ("updated_at");
CREATE INDEX "post_likes_created_at_idx" ON "post_likes" USING btree ("created_at");
ALTER TABLE "payload_locked_documents_rels" ADD CONSTRAINT "payload_locked_documents_rels_post_likes_fk" FOREIGN KEY ("post_likes_id") REFERENCES "public"."post_likes"("id") ON DELETE cascade ON UPDATE no action;
CREATE INDEX "payload_locked_documents_rels_post_likes_id_idx" ON "payload_locked_documents_rels" USING btree ("post_likes_id");`)
}
export async function down({ db, payload, req }: MigrateDownArgs): Promise<void> {
await db.execute(sql`
ALTER TABLE "post_likes" DISABLE ROW LEVEL SECURITY;
DROP TABLE "post_likes" CASCADE;
ALTER TABLE "payload_locked_documents_rels" DROP CONSTRAINT "payload_locked_documents_rels_post_likes_fk";
DROP INDEX "payload_locked_documents_rels_post_likes_id_idx";
ALTER TABLE "users" DROP COLUMN "full_name";
ALTER TABLE "users" DROP COLUMN "interest";
ALTER TABLE "users" DROP COLUMN "level";
ALTER TABLE "users" DROP COLUMN "goal";
ALTER TABLE "users" DROP COLUMN "prefs";
ALTER TABLE "users" DROP COLUMN "email_verified";
ALTER TABLE "users" DROP COLUMN "verification_token";
ALTER TABLE "users" DROP COLUMN "onboarded";
ALTER TABLE "posts" DROP COLUMN "level";
ALTER TABLE "posts" DROP COLUMN "views";
ALTER TABLE "posts" DROP COLUMN "likes";
ALTER TABLE "payload_locked_documents_rels" DROP COLUMN "post_likes_id";
DROP TYPE "public"."enum_users_interest";
DROP TYPE "public"."enum_users_level";
DROP TYPE "public"."enum_users_goal";
DROP TYPE "public"."enum_posts_level";`)
}
File diff suppressed because it is too large Load Diff
+13
View File
@@ -0,0 +1,13 @@
import { MigrateUpArgs, MigrateDownArgs, sql } from '@payloadcms/db-postgres'
export async function up({ db, payload, req }: MigrateUpArgs): Promise<void> {
await db.execute(sql`
ALTER TABLE "users" ADD COLUMN "terms_version" varchar;
ALTER TABLE "users" ADD COLUMN "terms_accepted_at" timestamp(3) with time zone;`)
}
export async function down({ db, payload, req }: MigrateDownArgs): Promise<void> {
await db.execute(sql`
ALTER TABLE "users" DROP COLUMN "terms_version";
ALTER TABLE "users" DROP COLUMN "terms_accepted_at";`)
}
File diff suppressed because it is too large Load Diff
+37
View File
@@ -0,0 +1,37 @@
import { MigrateUpArgs, MigrateDownArgs, sql } from '@payloadcms/db-postgres'
export async function up({ db, payload, req }: MigrateUpArgs): Promise<void> {
await db.execute(sql`
CREATE TYPE "public"."enum_comments_status" AS ENUM('pending', 'approved', 'rejected');
CREATE TABLE "comments" (
"id" serial PRIMARY KEY NOT NULL,
"post_id" integer NOT NULL,
"author_id" integer NOT NULL,
"author_name" varchar,
"body" varchar NOT NULL,
"status" "enum_comments_status" DEFAULT 'pending' NOT NULL,
"updated_at" timestamp(3) with time zone DEFAULT now() NOT NULL,
"created_at" timestamp(3) with time zone DEFAULT now() NOT NULL
);
ALTER TABLE "payload_locked_documents_rels" ADD COLUMN "comments_id" integer;
ALTER TABLE "comments" ADD CONSTRAINT "comments_post_id_posts_id_fk" FOREIGN KEY ("post_id") REFERENCES "public"."posts"("id") ON DELETE set null ON UPDATE no action;
ALTER TABLE "comments" ADD CONSTRAINT "comments_author_id_users_id_fk" FOREIGN KEY ("author_id") REFERENCES "public"."users"("id") ON DELETE set null ON UPDATE no action;
CREATE INDEX "comments_post_idx" ON "comments" USING btree ("post_id");
CREATE INDEX "comments_author_idx" ON "comments" USING btree ("author_id");
CREATE INDEX "comments_updated_at_idx" ON "comments" USING btree ("updated_at");
CREATE INDEX "comments_created_at_idx" ON "comments" USING btree ("created_at");
ALTER TABLE "payload_locked_documents_rels" ADD CONSTRAINT "payload_locked_documents_rels_comments_fk" FOREIGN KEY ("comments_id") REFERENCES "public"."comments"("id") ON DELETE cascade ON UPDATE no action;
CREATE INDEX "payload_locked_documents_rels_comments_id_idx" ON "payload_locked_documents_rels" USING btree ("comments_id");`)
}
export async function down({ db, payload, req }: MigrateDownArgs): Promise<void> {
await db.execute(sql`
ALTER TABLE "comments" DISABLE ROW LEVEL SECURITY;
DROP TABLE "comments" CASCADE;
ALTER TABLE "payload_locked_documents_rels" DROP CONSTRAINT "payload_locked_documents_rels_comments_fk";
DROP INDEX "payload_locked_documents_rels_comments_id_idx";
ALTER TABLE "payload_locked_documents_rels" DROP COLUMN "comments_id";
DROP TYPE "public"."enum_comments_status";`)
}
File diff suppressed because it is too large Load Diff
+56
View File
@@ -0,0 +1,56 @@
import { MigrateUpArgs, MigrateDownArgs, sql } from '@payloadcms/db-postgres'
export async function up({ db, payload, req }: MigrateUpArgs): Promise<void> {
await db.execute(sql`
CREATE TYPE "public"."enum_files_access" AS ENUM('free', 'vip');
CREATE TABLE "posts_rels" (
"id" serial PRIMARY KEY NOT NULL,
"order" integer,
"parent_id" integer NOT NULL,
"path" varchar NOT NULL,
"files_id" integer
);
CREATE TABLE "files" (
"id" serial PRIMARY KEY NOT NULL,
"label" varchar,
"access" "enum_files_access" DEFAULT 'vip' NOT NULL,
"updated_at" timestamp(3) with time zone DEFAULT now() NOT NULL,
"created_at" timestamp(3) with time zone DEFAULT now() NOT NULL,
"url" varchar,
"thumbnail_u_r_l" varchar,
"filename" varchar,
"mime_type" varchar,
"filesize" numeric,
"width" numeric,
"height" numeric,
"focal_x" numeric,
"focal_y" numeric
);
ALTER TABLE "payload_locked_documents_rels" ADD COLUMN "files_id" integer;
ALTER TABLE "posts_rels" ADD CONSTRAINT "posts_rels_parent_fk" FOREIGN KEY ("parent_id") REFERENCES "public"."posts"("id") ON DELETE cascade ON UPDATE no action;
ALTER TABLE "posts_rels" ADD CONSTRAINT "posts_rels_files_fk" FOREIGN KEY ("files_id") REFERENCES "public"."files"("id") ON DELETE cascade ON UPDATE no action;
CREATE INDEX "posts_rels_order_idx" ON "posts_rels" USING btree ("order");
CREATE INDEX "posts_rels_parent_idx" ON "posts_rels" USING btree ("parent_id");
CREATE INDEX "posts_rels_path_idx" ON "posts_rels" USING btree ("path");
CREATE INDEX "posts_rels_files_id_idx" ON "posts_rels" USING btree ("files_id");
CREATE INDEX "files_updated_at_idx" ON "files" USING btree ("updated_at");
CREATE INDEX "files_created_at_idx" ON "files" USING btree ("created_at");
CREATE UNIQUE INDEX "files_filename_idx" ON "files" USING btree ("filename");
ALTER TABLE "payload_locked_documents_rels" ADD CONSTRAINT "payload_locked_documents_rels_files_fk" FOREIGN KEY ("files_id") REFERENCES "public"."files"("id") ON DELETE cascade ON UPDATE no action;
CREATE INDEX "payload_locked_documents_rels_files_id_idx" ON "payload_locked_documents_rels" USING btree ("files_id");`)
}
export async function down({ db, payload, req }: MigrateDownArgs): Promise<void> {
await db.execute(sql`
ALTER TABLE "posts_rels" DISABLE ROW LEVEL SECURITY;
ALTER TABLE "files" DISABLE ROW LEVEL SECURITY;
DROP TABLE "posts_rels" CASCADE;
DROP TABLE "files" CASCADE;
ALTER TABLE "payload_locked_documents_rels" DROP CONSTRAINT "payload_locked_documents_rels_files_fk";
DROP INDEX "payload_locked_documents_rels_files_id_idx";
ALTER TABLE "payload_locked_documents_rels" DROP COLUMN "files_id";
DROP TYPE "public"."enum_files_access";`)
}
+39
View File
@@ -0,0 +1,39 @@
import * as migration_20261002_130505_init from './20261002_130505_init';
import * as migration_20261002_170000_add_full_name from './20261002_170000_add_full_name';
import * as migration_20261003_111451 from './20261003_111451';
import * as migration_20261003_133758 from './20261003_133758';
import * as migration_20261003_201454 from './20261003_201454';
import * as migration_20261009_130817 from './20261009_130817';
export const migrations = [
{
up: migration_20261002_130505_init.up,
down: migration_20261002_130505_init.down,
name: '20261002_130505_init',
},
{
up: migration_20261002_170000_add_full_name.up,
down: migration_20261002_170000_add_full_name.down,
name: '20261002_170000_add_full_name',
},
{
up: migration_20261003_111451.up,
down: migration_20261003_111451.down,
name: '20261003_111451',
},
{
up: migration_20261003_133758.up,
down: migration_20261003_133758.down,
name: '20261003_133758',
},
{
up: migration_20261003_201454.up,
down: migration_20261003_201454.down,
name: '20261003_201454',
},
{
up: migration_20261009_130817.up,
down: migration_20261009_130817.down,
name: '20261009_130817'
},
];