Compare commits

..

No commits in common. "main" and "template" have entirely different histories.

65 changed files with 1345 additions and 23387 deletions

View File

@ -2,17 +2,3 @@ DATABASE_URL=postgres://user:password@127.0.0.1:5432/postgres
REDIS_URL=redis://127.0.0.1:6379/ REDIS_URL=redis://127.0.0.1:6379/
PORT=8080 PORT=8080
HOST=127.0.0.1 HOST=127.0.0.1
PUBLIC_API_URL=http://localhost:3000
FRONTEND_URL=http://localhost:3002
SITES_HOST=http://localhost:3001
GOOGLE_CLIENT_ID=
GOOGLE_SECRET=
OPENAI_TOKEN=
CERTS_URL=
STRIPE_KEY=
STRIPE_WEBHOOK_SECRET=
STRIPE_SUBSCRIPTION_PRICE_ID=

View File

@ -1,21 +0,0 @@
FROM node:20-alpine
# Create app directory
WORKDIR /usr/src/app
# Install app dependencies
# A wildcard is used to ensure both package.json AND package-lock.json are copied
# where available (npm@5+) and yarn.lock because we are using yarn
COPY package*.json yarn.lock ./
# Run yarn without generating a yarn.lock file
RUN yarn --pure-lockfile
# Bundle app source
COPY . ./
# Use the port used by our server.js configuration
EXPOSE 3000
# This will run `yarn start` when the docker image is ran
CMD [ "yarn", "start" ]

View File

@ -1,12 +0,0 @@
import * as dotenv from 'dotenv'
dotenv.config()
export default {
"out": "./src/db/migrations",
"schema": "./src/db/schemas.js",
"dbCredentials": {
"url": process.env.DATABASE_URL
},
"dialect": "postgresql"
}

4
drizzle.config.json Normal file
View File

@ -0,0 +1,4 @@
{
"out": "./src/db/migrations",
"schema": "./src/db/schemas.ts"
}

10518
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -9,20 +9,19 @@
"license": "none", "license": "none",
"scripts": { "scripts": {
"lint": "xo", "lint": "xo",
"dev": "tsx watch --clear-screen=false src/index.js", "dev": "tsx watch --clear-screen=false src/index.ts",
"prettier": "prettier --write .", "prettier": "prettier --write .",
"migrate": "drizzle-kit generate:pg", "migrate": "drizzle-kit generate:pg",
"build": "tsc -p tsconfig.json && tsc-alias -p tsconfig.json",
"start": "node src/index.js" "start": "node src/index.js"
}, },
"devDependencies": { "devDependencies": {
"@types/cli-color": "^2.0.6", "@types/cli-color": "^2.0.6",
"@types/node": "^20.10.4", "@types/node": "^20.10.4",
"@types/pg": "^8.10.9", "@types/pg": "^8.10.9",
"@types/stripe-v3": "^3.1.33",
"cli-color": "^2.0.3", "cli-color": "^2.0.3",
"dotenv": "^16.3.1", "dotenv": "^16.3.1",
"drizzle-kit": "^0.22.4", "drizzle-kit": "^0.20.6",
"pg": "^8.12.0",
"prettier": "^3.1.1", "prettier": "^3.1.1",
"tsc-alias": "^1.8.8", "tsc-alias": "^1.8.8",
"tsx": "^3.12.7", "tsx": "^3.12.7",
@ -30,25 +29,13 @@
"xo": "^0.56.0" "xo": "^0.56.0"
}, },
"dependencies": { "dependencies": {
"@distube/ytdl-core": "^4.14.4",
"@fastify/cookie": "^9.3.1",
"@fastify/cors": "^8.4.2", "@fastify/cors": "^8.4.2",
"@fastify/multipart": "^8.2.0", "drizzle-orm": "^0.29.1",
"@fastify/oauth2": "^7.8.0",
"@neondatabase/serverless": "^0.9.3",
"@sentry/node": "^8.9.2",
"drizzle-orm": "^0.31.1",
"fastify": "^4.25.0", "fastify": "^4.25.0",
"fastify-plugin": "^4.5.1", "fastify-plugin": "^4.5.1",
"fastify-raw-body": "^4.3.0", "pg": "^8.11.3",
"googleapis": "^134.0.0",
"nodemailer": "^6.9.14",
"openai": "^4.38.5",
"redis": "^4.6.11", "redis": "^4.6.11",
"simple-get": "^4.0.1",
"stripe": "^15.7.0",
"tsc": "^2.0.4", "tsc": "^2.0.4",
"xml2js": "^0.6.2",
"zod": "^3.22.4" "zod": "^3.22.4"
}, },
"xo": { "xo": {

View File

@ -2,38 +2,39 @@ import * as schema from "./schemas.js";
import { env, Logger } from "../utils/index.js"; import { env, Logger } from "../utils/index.js";
import { drizzle } from "drizzle-orm/node-postgres"; import { drizzle } from "drizzle-orm/node-postgres";
import { migrate } from "drizzle-orm/node-postgres/migrator"; import { migrate } from "drizzle-orm/node-postgres/migrator";
import pg from 'pg' import pgpkg from 'pg';
const { Pool } = pg const { Pool } = pgpkg;
/** @type {ReturnType<typeof drizzle<typeof schema>>} */ /** @type {ReturnType<typeof drizzle<typeof schema>>} */
export let db; export let db;
export const initDb = async () => { export const initDb = async () => {
const client = new Pool({ const pool = await new Pool({
connectionString: env.DATABASE_URL, connectionString: env.DATABASE_URL,
keepAlive: true })
.connect()
.then((client) => {
Logger.info("INIT", "Connected to database");
return client;
})
.catch((error) => {
Logger.error("INIT", `Failed to connect to database ${String(error)}}`);
throw new Error(`Failed to connect to database ${String(error)}`);
}); });
client.on('error', e => { db = drizzle(pool, {
console.error('Database error', e);
client = null;
});
await client.connect();
db = drizzle(client, {
schema, schema,
}); });
await migrate(db, {
// await migrate(db, { migrationsFolder: "./src/db/migrations",
// migrationsFolder: "./src/db/migrations", })
// }) .then(() => {
// .then(() => { Logger.info("INIT", "Migrated database");
// Logger.info("INIT", "Migrated database"); })
// }) .catch((error) => {
// .catch((error) => { Logger.error("INIT", `Failed to migrate database ${String(error)}`);
// Logger.error("INIT", `Failed to migrate database ${String(error)}`); throw new Error(`Failed to migrate database ${String(error)}`);
// throw new Error(`Failed to migrate database ${String(error)}`); });
// });
}; };

View File

@ -1,99 +0,0 @@
-- Current sql file was generated after introspecting the database
-- If you want to run this migration please uncomment this code before executing migrations
/*
DO $$ BEGIN
CREATE TYPE "public"."subscription_tier" AS ENUM('free', 'basic', 'pro', 'enterprise');
EXCEPTION
WHEN duplicate_object THEN null;
END $$;
--> statement-breakpoint
CREATE TABLE IF NOT EXISTS "signups" (
"email" text NOT NULL,
"site_id" uuid NOT NULL,
"created_at" timestamp DEFAULT now() NOT NULL,
"source" text
);
--> statement-breakpoint
CREATE TABLE IF NOT EXISTS "users" (
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
"name" text,
"email" text,
"channel_id" text,
"google_id" text,
"uploads_playlist_id" text,
"subscription_tier" "subscription_tier" DEFAULT 'free' NOT NULL,
"tokens" integer DEFAULT 0 NOT NULL,
"stripe_id" text
);
--> statement-breakpoint
CREATE TABLE IF NOT EXISTS "sites" (
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
"user_id" uuid,
"name" text,
"primary_color_hex" varchar(6) DEFAULT 'c4ced4'::character varying NOT NULL,
"secondary_color_hex" varchar(6) DEFAULT '27251f'::character varying NOT NULL,
"text_color_hex" varchar(6) DEFAULT 'ffffff'::character varying NOT NULL,
"title" text DEFAULT 'The best blog in the world!',
"subtitle" text DEFAULT 'Some extra info about the best blog in the world!',
"domain" text,
"send_freebie" boolean DEFAULT false,
"freebie_name" text DEFAULT '',
"freebie_url" text DEFAULT '',
"freebie_text" text DEFAULT '',
"freebie_image_url" text DEFAULT '',
"subdomain_slug" text,
"social_medias" jsonb
);
--> statement-breakpoint
CREATE TABLE IF NOT EXISTS "sessions" (
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
"user_id" uuid,
"google_access_token" text,
"google_refresh_token" text,
"expires_at" timestamp
);
--> statement-breakpoint
CREATE TABLE IF NOT EXISTS "articles" (
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
"site_id" uuid,
"content" text,
"source_video_id" text,
"title" text,
"seo_slug" text,
"created_at" timestamp DEFAULT now(),
"is_public" boolean DEFAULT false,
"views" integer DEFAULT 0,
"seo_title" text,
"seo_description" text,
"excerp" text
);
--> statement-breakpoint
CREATE TABLE IF NOT EXISTS "waitlist" (
"email" text
);
--> statement-breakpoint
DO $$ BEGIN
ALTER TABLE "signups" ADD CONSTRAINT "signups_site_id_sites_id_fk" FOREIGN KEY ("site_id") REFERENCES "public"."sites"("id") ON DELETE no action ON UPDATE no action;
EXCEPTION
WHEN duplicate_object THEN null;
END $$;
--> statement-breakpoint
DO $$ BEGIN
ALTER TABLE "sites" ADD CONSTRAINT "sites_user_id_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."users"("id") ON DELETE no action ON UPDATE no action;
EXCEPTION
WHEN duplicate_object THEN null;
END $$;
--> statement-breakpoint
DO $$ BEGIN
ALTER TABLE "sessions" ADD CONSTRAINT "sessions_user_id_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."users"("id") ON DELETE no action ON UPDATE no action;
EXCEPTION
WHEN duplicate_object THEN null;
END $$;
--> statement-breakpoint
DO $$ BEGIN
ALTER TABLE "articles" ADD CONSTRAINT "articles_site_id_sites_id_fk" FOREIGN KEY ("site_id") REFERENCES "public"."sites"("id") ON DELETE no action ON UPDATE no action;
EXCEPTION
WHEN duplicate_object THEN null;
END $$;
*/

View File

@ -0,0 +1,5 @@
CREATE TABLE IF NOT EXISTS "users" (
"id" serial PRIMARY KEY NOT NULL,
"full_name" text,
"phone" varchar(256)
);

View File

@ -1,5 +0,0 @@
ALTER TABLE "sites" ALTER COLUMN "primary_color_hex" SET DEFAULT 'c4ced4';--> statement-breakpoint
ALTER TABLE "sites" ALTER COLUMN "secondary_color_hex" SET DEFAULT '27251f';--> statement-breakpoint
ALTER TABLE "sites" ALTER COLUMN "text_color_hex" SET DEFAULT 'ffffff';--> statement-breakpoint
ALTER TABLE "sites" ALTER COLUMN "domain" SET DEFAULT null;--> statement-breakpoint
CREATE UNIQUE INDEX IF NOT EXISTS "domainUniqueIndex" ON "sites" USING btree ((lower("domain"))) WHERE "sites"."domain" IS NOT NULL;

View File

@ -1 +0,0 @@
ALTER TABLE "sites" ALTER COLUMN "domain" SET DEFAULT NULL;

View File

@ -1,2 +0,0 @@
DROP INDEX IF EXISTS "domainUniqueIndex";--> statement-breakpoint
ALTER TABLE "sites" ADD CONSTRAINT "domainUniqueIndex" UNIQUE NULLS NOT DISTINCT("domain");

View File

@ -1,2 +0,0 @@
ALTER TABLE "sites" DROP CONSTRAINT "domainUniqueIndex";--> statement-breakpoint
ALTER TABLE "sites" ADD CONSTRAINT "domainUniqueIndex" UNIQUE("domain");

View File

@ -1,2 +0,0 @@
ALTER TABLE "sites" DROP CONSTRAINT "domainUniqueIndex";--> statement-breakpoint
ALTER TABLE "sites" ADD CONSTRAINT "sites_domain_unique" UNIQUE("domain");

View File

@ -1,2 +0,0 @@
ALTER TABLE "users" ALTER COLUMN "tokens" SET DEFAULT 200;--> statement-breakpoint
ALTER TABLE "sites" ADD COLUMN "auto_publish" boolean DEFAULT false;

View File

@ -1 +0,0 @@
ALTER TABLE "sites" ADD COLUMN "pubsub_expiry" date;

View File

@ -1 +0,0 @@
ALTER TABLE "sites" ALTER COLUMN "pubsub_expiry" SET DATA TYPE timestamp;

View File

@ -1,7 +0,0 @@
DO $$ BEGIN
CREATE TYPE "public"."article_status" AS ENUM('queued', 'transcribing', 'generating', 'done');
EXCEPTION
WHEN duplicate_object THEN null;
END $$;
--> statement-breakpoint
ALTER TABLE "articles" ADD COLUMN "status" "article_status" DEFAULT 'done';

View File

@ -1 +0,0 @@
ALTER TABLE "articles" ALTER COLUMN "status" SET DEFAULT 'queued';

View File

@ -1 +0,0 @@
ALTER TYPE "article_status" ADD VALUE 'error';

View File

@ -1,3 +0,0 @@
ALTER TABLE "sites" ALTER COLUMN "freebie_name" SET DEFAULT 'Our newsletter';--> statement-breakpoint
ALTER TABLE "sites" ADD COLUMN "use_contact_page" boolean DEFAULT false;--> statement-breakpoint
ALTER TABLE "sites" ADD COLUMN "contact_email" text;

View File

@ -1,2 +0,0 @@
ALTER TABLE "sites" ADD COLUMN "use_about_page" boolean DEFAULT true;--> statement-breakpoint
ALTER TABLE "sites" ADD COLUMN "about_text" text;

View File

@ -1,2 +0,0 @@
ALTER TABLE "users" ADD COLUMN "subscribed_until" date DEFAULT '1970-01-01';--> statement-breakpoint
ALTER TABLE "users" ADD COLUMN "tokens_claimed" date;

View File

@ -1 +0,0 @@
ALTER TABLE "users" ADD COLUMN "trial_used" boolean DEFAULT false;

View File

@ -1,452 +1,42 @@
{ {
"id": "00000000-0000-0000-0000-000000000000", "version": "5",
"prevId": "", "dialect": "pg",
"version": "7", "id": "478200dd-a614-41dd-9cd1-f6368405417e",
"dialect": "postgresql", "prevId": "00000000-0000-0000-0000-000000000000",
"tables": { "tables": {
"public.signups": { "users": {
"name": "signups",
"schema": "",
"columns": {
"email": {
"name": "email",
"type": "text",
"primaryKey": false,
"notNull": true
},
"site_id": {
"name": "site_id",
"type": "uuid",
"primaryKey": false,
"notNull": true
},
"created_at": {
"name": "created_at",
"type": "timestamp",
"primaryKey": false,
"notNull": true,
"default": "now()"
},
"source": {
"name": "source",
"type": "text",
"primaryKey": false,
"notNull": false
}
},
"indexes": {},
"foreignKeys": {
"signups_site_id_sites_id_fk": {
"name": "signups_site_id_sites_id_fk",
"tableFrom": "signups",
"tableTo": "sites",
"schemaTo": "public",
"columnsFrom": [
"site_id"
],
"columnsTo": [
"id"
],
"onDelete": "no action",
"onUpdate": "no action"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {}
},
"public.users": {
"name": "users", "name": "users",
"schema": "", "schema": "",
"columns": { "columns": {
"id": { "id": {
"name": "id", "name": "id",
"type": "uuid", "type": "serial",
"primaryKey": true, "primaryKey": true,
"notNull": true, "notNull": true
"default": "gen_random_uuid()"
}, },
"name": { "full_name": {
"name": "name", "name": "full_name",
"type": "text", "type": "text",
"primaryKey": false, "primaryKey": false,
"notNull": false "notNull": false
}, },
"email": { "phone": {
"name": "email", "name": "phone",
"type": "text", "type": "varchar(256)",
"primaryKey": false,
"notNull": false
},
"channel_id": {
"name": "channel_id",
"type": "text",
"primaryKey": false,
"notNull": false
},
"google_id": {
"name": "google_id",
"type": "text",
"primaryKey": false,
"notNull": false
},
"uploads_playlist_id": {
"name": "uploads_playlist_id",
"type": "text",
"primaryKey": false,
"notNull": false
},
"subscription_tier": {
"name": "subscription_tier",
"type": "subscription_tier",
"typeSchema": "public",
"primaryKey": false,
"notNull": true,
"default": "'free'"
},
"tokens": {
"name": "tokens",
"type": "integer",
"primaryKey": false,
"notNull": true,
"default": 0
},
"stripe_id": {
"name": "stripe_id",
"type": "text",
"primaryKey": false, "primaryKey": false,
"notNull": false "notNull": false
} }
}, },
"indexes": {}, "indexes": {},
"foreignKeys": {}, "foreignKeys": {},
"compositePrimaryKeys": {}, "compositePrimaryKeys": {}
"uniqueConstraints": {}
},
"public.sites": {
"name": "sites",
"schema": "",
"columns": {
"id": {
"name": "id",
"type": "uuid",
"primaryKey": true,
"notNull": true,
"default": "gen_random_uuid()"
},
"user_id": {
"name": "user_id",
"type": "uuid",
"primaryKey": false,
"notNull": false
},
"name": {
"name": "name",
"type": "text",
"primaryKey": false,
"notNull": false
},
"primary_color_hex": {
"name": "primary_color_hex",
"type": "varchar(6)",
"primaryKey": false,
"notNull": true,
"default": "'c4ced4'::character varying"
},
"secondary_color_hex": {
"name": "secondary_color_hex",
"type": "varchar(6)",
"primaryKey": false,
"notNull": true,
"default": "'27251f'::character varying"
},
"text_color_hex": {
"name": "text_color_hex",
"type": "varchar(6)",
"primaryKey": false,
"notNull": true,
"default": "'ffffff'::character varying"
},
"title": {
"name": "title",
"type": "text",
"primaryKey": false,
"notNull": false,
"default": "'The best blog in the world!'"
},
"subtitle": {
"name": "subtitle",
"type": "text",
"primaryKey": false,
"notNull": false,
"default": "'Some extra info about the best blog in the world!'"
},
"domain": {
"name": "domain",
"type": "text",
"primaryKey": false,
"notNull": false
},
"send_freebie": {
"name": "send_freebie",
"type": "boolean",
"primaryKey": false,
"notNull": false,
"default": false
},
"freebie_name": {
"name": "freebie_name",
"type": "text",
"primaryKey": false,
"notNull": false,
"default": "''"
},
"freebie_url": {
"name": "freebie_url",
"type": "text",
"primaryKey": false,
"notNull": false,
"default": "''"
},
"freebie_text": {
"name": "freebie_text",
"type": "text",
"primaryKey": false,
"notNull": false,
"default": "''"
},
"freebie_image_url": {
"name": "freebie_image_url",
"type": "text",
"primaryKey": false,
"notNull": false,
"default": "''"
},
"subdomain_slug": {
"name": "subdomain_slug",
"type": "text",
"primaryKey": false,
"notNull": false
},
"social_medias": {
"name": "social_medias",
"type": "jsonb",
"primaryKey": false,
"notNull": false
}
},
"indexes": {},
"foreignKeys": {
"sites_user_id_users_id_fk": {
"name": "sites_user_id_users_id_fk",
"tableFrom": "sites",
"tableTo": "users",
"schemaTo": "public",
"columnsFrom": [
"user_id"
],
"columnsTo": [
"id"
],
"onDelete": "no action",
"onUpdate": "no action"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {}
},
"public.sessions": {
"name": "sessions",
"schema": "",
"columns": {
"id": {
"name": "id",
"type": "uuid",
"primaryKey": true,
"notNull": true,
"default": "gen_random_uuid()"
},
"user_id": {
"name": "user_id",
"type": "uuid",
"primaryKey": false,
"notNull": false
},
"google_access_token": {
"name": "google_access_token",
"type": "text",
"primaryKey": false,
"notNull": false
},
"google_refresh_token": {
"name": "google_refresh_token",
"type": "text",
"primaryKey": false,
"notNull": false
},
"expires_at": {
"name": "expires_at",
"type": "timestamp",
"primaryKey": false,
"notNull": false
}
},
"indexes": {},
"foreignKeys": {
"sessions_user_id_users_id_fk": {
"name": "sessions_user_id_users_id_fk",
"tableFrom": "sessions",
"tableTo": "users",
"schemaTo": "public",
"columnsFrom": [
"user_id"
],
"columnsTo": [
"id"
],
"onDelete": "no action",
"onUpdate": "no action"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {}
},
"public.articles": {
"name": "articles",
"schema": "",
"columns": {
"id": {
"name": "id",
"type": "uuid",
"primaryKey": true,
"notNull": true,
"default": "gen_random_uuid()"
},
"site_id": {
"name": "site_id",
"type": "uuid",
"primaryKey": false,
"notNull": false
},
"content": {
"name": "content",
"type": "text",
"primaryKey": false,
"notNull": false
},
"source_video_id": {
"name": "source_video_id",
"type": "text",
"primaryKey": false,
"notNull": false
},
"title": {
"name": "title",
"type": "text",
"primaryKey": false,
"notNull": false
},
"seo_slug": {
"name": "seo_slug",
"type": "text",
"primaryKey": false,
"notNull": false
},
"created_at": {
"name": "created_at",
"type": "timestamp",
"primaryKey": false,
"notNull": false,
"default": "now()"
},
"is_public": {
"name": "is_public",
"type": "boolean",
"primaryKey": false,
"notNull": false,
"default": false
},
"views": {
"name": "views",
"type": "integer",
"primaryKey": false,
"notNull": false,
"default": 0
},
"seo_title": {
"name": "seo_title",
"type": "text",
"primaryKey": false,
"notNull": false
},
"seo_description": {
"name": "seo_description",
"type": "text",
"primaryKey": false,
"notNull": false
},
"excerp": {
"name": "excerp",
"type": "text",
"primaryKey": false,
"notNull": false
}
},
"indexes": {},
"foreignKeys": {
"articles_site_id_sites_id_fk": {
"name": "articles_site_id_sites_id_fk",
"tableFrom": "articles",
"tableTo": "sites",
"schemaTo": "public",
"columnsFrom": [
"site_id"
],
"columnsTo": [
"id"
],
"onDelete": "no action",
"onUpdate": "no action"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {}
},
"public.waitlist": {
"name": "waitlist",
"schema": "",
"columns": {
"email": {
"name": "email",
"type": "text",
"primaryKey": false,
"notNull": false
}
},
"indexes": {},
"foreignKeys": {},
"compositePrimaryKeys": {},
"uniqueConstraints": {}
}
},
"enums": {
"public.subscription_tier": {
"name": "subscription_tier",
"values": [
"free",
"basic",
"pro",
"enterprise"
],
"schema": "public"
} }
}, },
"enums": {},
"schemas": {}, "schemas": {},
"_meta": { "_meta": {
"schemas": {}, "schemas": {},
"tables": {}, "tables": {},
"columns": {} "columns": {}
},
"internal": {
"tables": {}
} }
} }

View File

@ -1,463 +0,0 @@
{
"id": "4bc82cd1-68f2-4b03-bdc9-058a4f3ff395",
"prevId": "00000000-0000-0000-0000-000000000000",
"version": "7",
"dialect": "postgresql",
"tables": {
"public.articles": {
"name": "articles",
"schema": "",
"columns": {
"id": {
"name": "id",
"type": "uuid",
"primaryKey": true,
"notNull": true,
"default": "gen_random_uuid()"
},
"site_id": {
"name": "site_id",
"type": "uuid",
"primaryKey": false,
"notNull": false
},
"content": {
"name": "content",
"type": "text",
"primaryKey": false,
"notNull": false
},
"source_video_id": {
"name": "source_video_id",
"type": "text",
"primaryKey": false,
"notNull": false
},
"title": {
"name": "title",
"type": "text",
"primaryKey": false,
"notNull": false
},
"seo_slug": {
"name": "seo_slug",
"type": "text",
"primaryKey": false,
"notNull": false
},
"is_public": {
"name": "is_public",
"type": "boolean",
"primaryKey": false,
"notNull": false,
"default": false
},
"views": {
"name": "views",
"type": "integer",
"primaryKey": false,
"notNull": false,
"default": 0
},
"seo_title": {
"name": "seo_title",
"type": "text",
"primaryKey": false,
"notNull": false
},
"seo_description": {
"name": "seo_description",
"type": "text",
"primaryKey": false,
"notNull": false
},
"excerp": {
"name": "excerp",
"type": "text",
"primaryKey": false,
"notNull": false
},
"created_at": {
"name": "created_at",
"type": "timestamp",
"primaryKey": false,
"notNull": false,
"default": "now()"
}
},
"indexes": {},
"foreignKeys": {
"articles_site_id_sites_id_fk": {
"name": "articles_site_id_sites_id_fk",
"tableFrom": "articles",
"tableTo": "sites",
"columnsFrom": [
"site_id"
],
"columnsTo": [
"id"
],
"onDelete": "no action",
"onUpdate": "no action"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {}
},
"public.sessions": {
"name": "sessions",
"schema": "",
"columns": {
"id": {
"name": "id",
"type": "uuid",
"primaryKey": true,
"notNull": true,
"default": "gen_random_uuid()"
},
"user_id": {
"name": "user_id",
"type": "uuid",
"primaryKey": false,
"notNull": false
},
"google_access_token": {
"name": "google_access_token",
"type": "text",
"primaryKey": false,
"notNull": false
},
"google_refresh_token": {
"name": "google_refresh_token",
"type": "text",
"primaryKey": false,
"notNull": false
},
"expires_at": {
"name": "expires_at",
"type": "timestamp",
"primaryKey": false,
"notNull": false
}
},
"indexes": {},
"foreignKeys": {
"sessions_user_id_users_id_fk": {
"name": "sessions_user_id_users_id_fk",
"tableFrom": "sessions",
"tableTo": "users",
"columnsFrom": [
"user_id"
],
"columnsTo": [
"id"
],
"onDelete": "no action",
"onUpdate": "no action"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {}
},
"public.signups": {
"name": "signups",
"schema": "",
"columns": {
"email": {
"name": "email",
"type": "text",
"primaryKey": false,
"notNull": true
},
"site_id": {
"name": "site_id",
"type": "uuid",
"primaryKey": false,
"notNull": true
},
"source": {
"name": "source",
"type": "text",
"primaryKey": false,
"notNull": false
},
"created_at": {
"name": "created_at",
"type": "timestamp",
"primaryKey": false,
"notNull": true,
"default": "now()"
}
},
"indexes": {},
"foreignKeys": {
"signups_site_id_sites_id_fk": {
"name": "signups_site_id_sites_id_fk",
"tableFrom": "signups",
"tableTo": "sites",
"columnsFrom": [
"site_id"
],
"columnsTo": [
"id"
],
"onDelete": "no action",
"onUpdate": "no action"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {}
},
"public.sites": {
"name": "sites",
"schema": "",
"columns": {
"id": {
"name": "id",
"type": "uuid",
"primaryKey": true,
"notNull": true,
"default": "gen_random_uuid()"
},
"user_id": {
"name": "user_id",
"type": "uuid",
"primaryKey": false,
"notNull": false
},
"name": {
"name": "name",
"type": "text",
"primaryKey": false,
"notNull": false
},
"primary_color_hex": {
"name": "primary_color_hex",
"type": "varchar(6)",
"primaryKey": false,
"notNull": true,
"default": "'c4ced4'"
},
"secondary_color_hex": {
"name": "secondary_color_hex",
"type": "varchar(6)",
"primaryKey": false,
"notNull": true,
"default": "'27251f'"
},
"text_color_hex": {
"name": "text_color_hex",
"type": "varchar(6)",
"primaryKey": false,
"notNull": true,
"default": "'ffffff'"
},
"title": {
"name": "title",
"type": "text",
"primaryKey": false,
"notNull": false,
"default": "'The best blog in the world!'"
},
"subtitle": {
"name": "subtitle",
"type": "text",
"primaryKey": false,
"notNull": false,
"default": "'Some extra info about the best blog in the world!'"
},
"domain": {
"name": "domain",
"type": "text",
"primaryKey": false,
"notNull": false,
"default": null
},
"send_freebie": {
"name": "send_freebie",
"type": "boolean",
"primaryKey": false,
"notNull": false,
"default": false
},
"freebie_name": {
"name": "freebie_name",
"type": "text",
"primaryKey": false,
"notNull": false,
"default": "''"
},
"freebie_url": {
"name": "freebie_url",
"type": "text",
"primaryKey": false,
"notNull": false,
"default": "''"
},
"freebie_text": {
"name": "freebie_text",
"type": "text",
"primaryKey": false,
"notNull": false,
"default": "''"
},
"freebie_image_url": {
"name": "freebie_image_url",
"type": "text",
"primaryKey": false,
"notNull": false,
"default": "''"
},
"subdomain_slug": {
"name": "subdomain_slug",
"type": "text",
"primaryKey": false,
"notNull": false
},
"social_medias": {
"name": "social_medias",
"type": "jsonb",
"primaryKey": false,
"notNull": false
}
},
"indexes": {
"domainUniqueIndex": {
"name": "domainUniqueIndex",
"columns": [
{
"expression": "(lower(\"domain\"))",
"asc": true,
"isExpression": true,
"nulls": "last"
}
],
"isUnique": true,
"where": "\"sites\".\"domain\" IS NOT NULL",
"concurrently": false,
"method": "btree",
"with": {}
}
},
"foreignKeys": {
"sites_user_id_users_id_fk": {
"name": "sites_user_id_users_id_fk",
"tableFrom": "sites",
"tableTo": "users",
"columnsFrom": [
"user_id"
],
"columnsTo": [
"id"
],
"onDelete": "no action",
"onUpdate": "no action"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {}
},
"public.users": {
"name": "users",
"schema": "",
"columns": {
"id": {
"name": "id",
"type": "uuid",
"primaryKey": true,
"notNull": true,
"default": "gen_random_uuid()"
},
"google_id": {
"name": "google_id",
"type": "text",
"primaryKey": false,
"notNull": false
},
"stripe_id": {
"name": "stripe_id",
"type": "text",
"primaryKey": false,
"notNull": false
},
"name": {
"name": "name",
"type": "text",
"primaryKey": false,
"notNull": false
},
"email": {
"name": "email",
"type": "text",
"primaryKey": false,
"notNull": false
},
"channel_id": {
"name": "channel_id",
"type": "text",
"primaryKey": false,
"notNull": false
},
"uploads_playlist_id": {
"name": "uploads_playlist_id",
"type": "text",
"primaryKey": false,
"notNull": false
},
"subscription_tier": {
"name": "subscription_tier",
"type": "subscription_tier",
"typeSchema": "public",
"primaryKey": false,
"notNull": true,
"default": "'free'"
},
"tokens": {
"name": "tokens",
"type": "integer",
"primaryKey": false,
"notNull": true,
"default": 0
}
},
"indexes": {},
"foreignKeys": {},
"compositePrimaryKeys": {},
"uniqueConstraints": {}
},
"public.waitlist": {
"name": "waitlist",
"schema": "",
"columns": {
"email": {
"name": "email",
"type": "text",
"primaryKey": false,
"notNull": false
}
},
"indexes": {},
"foreignKeys": {},
"compositePrimaryKeys": {},
"uniqueConstraints": {}
}
},
"enums": {
"public.subscription_tier": {
"name": "subscription_tier",
"schema": "public",
"values": [
"free",
"basic",
"pro",
"enterprise"
]
}
},
"schemas": {},
"_meta": {
"columns": {},
"schemas": {},
"tables": {}
}
}

View File

@ -1,463 +0,0 @@
{
"id": "ca4576ea-6a09-4a8d-afe2-ad00dd56505f",
"prevId": "4bc82cd1-68f2-4b03-bdc9-058a4f3ff395",
"version": "7",
"dialect": "postgresql",
"tables": {
"public.articles": {
"name": "articles",
"schema": "",
"columns": {
"id": {
"name": "id",
"type": "uuid",
"primaryKey": true,
"notNull": true,
"default": "gen_random_uuid()"
},
"site_id": {
"name": "site_id",
"type": "uuid",
"primaryKey": false,
"notNull": false
},
"content": {
"name": "content",
"type": "text",
"primaryKey": false,
"notNull": false
},
"source_video_id": {
"name": "source_video_id",
"type": "text",
"primaryKey": false,
"notNull": false
},
"title": {
"name": "title",
"type": "text",
"primaryKey": false,
"notNull": false
},
"seo_slug": {
"name": "seo_slug",
"type": "text",
"primaryKey": false,
"notNull": false
},
"is_public": {
"name": "is_public",
"type": "boolean",
"primaryKey": false,
"notNull": false,
"default": false
},
"views": {
"name": "views",
"type": "integer",
"primaryKey": false,
"notNull": false,
"default": 0
},
"seo_title": {
"name": "seo_title",
"type": "text",
"primaryKey": false,
"notNull": false
},
"seo_description": {
"name": "seo_description",
"type": "text",
"primaryKey": false,
"notNull": false
},
"excerp": {
"name": "excerp",
"type": "text",
"primaryKey": false,
"notNull": false
},
"created_at": {
"name": "created_at",
"type": "timestamp",
"primaryKey": false,
"notNull": false,
"default": "now()"
}
},
"indexes": {},
"foreignKeys": {
"articles_site_id_sites_id_fk": {
"name": "articles_site_id_sites_id_fk",
"tableFrom": "articles",
"tableTo": "sites",
"columnsFrom": [
"site_id"
],
"columnsTo": [
"id"
],
"onDelete": "no action",
"onUpdate": "no action"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {}
},
"public.sessions": {
"name": "sessions",
"schema": "",
"columns": {
"id": {
"name": "id",
"type": "uuid",
"primaryKey": true,
"notNull": true,
"default": "gen_random_uuid()"
},
"user_id": {
"name": "user_id",
"type": "uuid",
"primaryKey": false,
"notNull": false
},
"google_access_token": {
"name": "google_access_token",
"type": "text",
"primaryKey": false,
"notNull": false
},
"google_refresh_token": {
"name": "google_refresh_token",
"type": "text",
"primaryKey": false,
"notNull": false
},
"expires_at": {
"name": "expires_at",
"type": "timestamp",
"primaryKey": false,
"notNull": false
}
},
"indexes": {},
"foreignKeys": {
"sessions_user_id_users_id_fk": {
"name": "sessions_user_id_users_id_fk",
"tableFrom": "sessions",
"tableTo": "users",
"columnsFrom": [
"user_id"
],
"columnsTo": [
"id"
],
"onDelete": "no action",
"onUpdate": "no action"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {}
},
"public.signups": {
"name": "signups",
"schema": "",
"columns": {
"email": {
"name": "email",
"type": "text",
"primaryKey": false,
"notNull": true
},
"site_id": {
"name": "site_id",
"type": "uuid",
"primaryKey": false,
"notNull": true
},
"source": {
"name": "source",
"type": "text",
"primaryKey": false,
"notNull": false
},
"created_at": {
"name": "created_at",
"type": "timestamp",
"primaryKey": false,
"notNull": true,
"default": "now()"
}
},
"indexes": {},
"foreignKeys": {
"signups_site_id_sites_id_fk": {
"name": "signups_site_id_sites_id_fk",
"tableFrom": "signups",
"tableTo": "sites",
"columnsFrom": [
"site_id"
],
"columnsTo": [
"id"
],
"onDelete": "no action",
"onUpdate": "no action"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {}
},
"public.sites": {
"name": "sites",
"schema": "",
"columns": {
"id": {
"name": "id",
"type": "uuid",
"primaryKey": true,
"notNull": true,
"default": "gen_random_uuid()"
},
"user_id": {
"name": "user_id",
"type": "uuid",
"primaryKey": false,
"notNull": false
},
"name": {
"name": "name",
"type": "text",
"primaryKey": false,
"notNull": false
},
"primary_color_hex": {
"name": "primary_color_hex",
"type": "varchar(6)",
"primaryKey": false,
"notNull": true,
"default": "'c4ced4'"
},
"secondary_color_hex": {
"name": "secondary_color_hex",
"type": "varchar(6)",
"primaryKey": false,
"notNull": true,
"default": "'27251f'"
},
"text_color_hex": {
"name": "text_color_hex",
"type": "varchar(6)",
"primaryKey": false,
"notNull": true,
"default": "'ffffff'"
},
"title": {
"name": "title",
"type": "text",
"primaryKey": false,
"notNull": false,
"default": "'The best blog in the world!'"
},
"subtitle": {
"name": "subtitle",
"type": "text",
"primaryKey": false,
"notNull": false,
"default": "'Some extra info about the best blog in the world!'"
},
"domain": {
"name": "domain",
"type": "text",
"primaryKey": false,
"notNull": false,
"default": "NULL"
},
"send_freebie": {
"name": "send_freebie",
"type": "boolean",
"primaryKey": false,
"notNull": false,
"default": false
},
"freebie_name": {
"name": "freebie_name",
"type": "text",
"primaryKey": false,
"notNull": false,
"default": "''"
},
"freebie_url": {
"name": "freebie_url",
"type": "text",
"primaryKey": false,
"notNull": false,
"default": "''"
},
"freebie_text": {
"name": "freebie_text",
"type": "text",
"primaryKey": false,
"notNull": false,
"default": "''"
},
"freebie_image_url": {
"name": "freebie_image_url",
"type": "text",
"primaryKey": false,
"notNull": false,
"default": "''"
},
"subdomain_slug": {
"name": "subdomain_slug",
"type": "text",
"primaryKey": false,
"notNull": false
},
"social_medias": {
"name": "social_medias",
"type": "jsonb",
"primaryKey": false,
"notNull": false
}
},
"indexes": {
"domainUniqueIndex": {
"name": "domainUniqueIndex",
"columns": [
{
"expression": "(lower(\"domain\"))",
"asc": true,
"isExpression": true,
"nulls": "last"
}
],
"isUnique": true,
"where": "\"sites\".\"domain\" IS NOT NULL",
"concurrently": false,
"method": "btree",
"with": {}
}
},
"foreignKeys": {
"sites_user_id_users_id_fk": {
"name": "sites_user_id_users_id_fk",
"tableFrom": "sites",
"tableTo": "users",
"columnsFrom": [
"user_id"
],
"columnsTo": [
"id"
],
"onDelete": "no action",
"onUpdate": "no action"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {}
},
"public.users": {
"name": "users",
"schema": "",
"columns": {
"id": {
"name": "id",
"type": "uuid",
"primaryKey": true,
"notNull": true,
"default": "gen_random_uuid()"
},
"google_id": {
"name": "google_id",
"type": "text",
"primaryKey": false,
"notNull": false
},
"stripe_id": {
"name": "stripe_id",
"type": "text",
"primaryKey": false,
"notNull": false
},
"name": {
"name": "name",
"type": "text",
"primaryKey": false,
"notNull": false
},
"email": {
"name": "email",
"type": "text",
"primaryKey": false,
"notNull": false
},
"channel_id": {
"name": "channel_id",
"type": "text",
"primaryKey": false,
"notNull": false
},
"uploads_playlist_id": {
"name": "uploads_playlist_id",
"type": "text",
"primaryKey": false,
"notNull": false
},
"subscription_tier": {
"name": "subscription_tier",
"type": "subscription_tier",
"typeSchema": "public",
"primaryKey": false,
"notNull": true,
"default": "'free'"
},
"tokens": {
"name": "tokens",
"type": "integer",
"primaryKey": false,
"notNull": true,
"default": 0
}
},
"indexes": {},
"foreignKeys": {},
"compositePrimaryKeys": {},
"uniqueConstraints": {}
},
"public.waitlist": {
"name": "waitlist",
"schema": "",
"columns": {
"email": {
"name": "email",
"type": "text",
"primaryKey": false,
"notNull": false
}
},
"indexes": {},
"foreignKeys": {},
"compositePrimaryKeys": {},
"uniqueConstraints": {}
}
},
"enums": {
"public.subscription_tier": {
"name": "subscription_tier",
"schema": "public",
"values": [
"free",
"basic",
"pro",
"enterprise"
]
}
},
"schemas": {},
"_meta": {
"columns": {},
"schemas": {},
"tables": {}
}
}

View File

@ -1,454 +0,0 @@
{
"id": "a8542e87-bade-4eae-b740-8ca9d8429118",
"prevId": "ca4576ea-6a09-4a8d-afe2-ad00dd56505f",
"version": "7",
"dialect": "postgresql",
"tables": {
"public.articles": {
"name": "articles",
"schema": "",
"columns": {
"id": {
"name": "id",
"type": "uuid",
"primaryKey": true,
"notNull": true,
"default": "gen_random_uuid()"
},
"site_id": {
"name": "site_id",
"type": "uuid",
"primaryKey": false,
"notNull": false
},
"content": {
"name": "content",
"type": "text",
"primaryKey": false,
"notNull": false
},
"source_video_id": {
"name": "source_video_id",
"type": "text",
"primaryKey": false,
"notNull": false
},
"title": {
"name": "title",
"type": "text",
"primaryKey": false,
"notNull": false
},
"seo_slug": {
"name": "seo_slug",
"type": "text",
"primaryKey": false,
"notNull": false
},
"is_public": {
"name": "is_public",
"type": "boolean",
"primaryKey": false,
"notNull": false,
"default": false
},
"views": {
"name": "views",
"type": "integer",
"primaryKey": false,
"notNull": false,
"default": 0
},
"seo_title": {
"name": "seo_title",
"type": "text",
"primaryKey": false,
"notNull": false
},
"seo_description": {
"name": "seo_description",
"type": "text",
"primaryKey": false,
"notNull": false
},
"excerp": {
"name": "excerp",
"type": "text",
"primaryKey": false,
"notNull": false
},
"created_at": {
"name": "created_at",
"type": "timestamp",
"primaryKey": false,
"notNull": false,
"default": "now()"
}
},
"indexes": {},
"foreignKeys": {
"articles_site_id_sites_id_fk": {
"name": "articles_site_id_sites_id_fk",
"tableFrom": "articles",
"tableTo": "sites",
"columnsFrom": [
"site_id"
],
"columnsTo": [
"id"
],
"onDelete": "no action",
"onUpdate": "no action"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {}
},
"public.sessions": {
"name": "sessions",
"schema": "",
"columns": {
"id": {
"name": "id",
"type": "uuid",
"primaryKey": true,
"notNull": true,
"default": "gen_random_uuid()"
},
"user_id": {
"name": "user_id",
"type": "uuid",
"primaryKey": false,
"notNull": false
},
"google_access_token": {
"name": "google_access_token",
"type": "text",
"primaryKey": false,
"notNull": false
},
"google_refresh_token": {
"name": "google_refresh_token",
"type": "text",
"primaryKey": false,
"notNull": false
},
"expires_at": {
"name": "expires_at",
"type": "timestamp",
"primaryKey": false,
"notNull": false
}
},
"indexes": {},
"foreignKeys": {
"sessions_user_id_users_id_fk": {
"name": "sessions_user_id_users_id_fk",
"tableFrom": "sessions",
"tableTo": "users",
"columnsFrom": [
"user_id"
],
"columnsTo": [
"id"
],
"onDelete": "no action",
"onUpdate": "no action"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {}
},
"public.signups": {
"name": "signups",
"schema": "",
"columns": {
"email": {
"name": "email",
"type": "text",
"primaryKey": false,
"notNull": true
},
"site_id": {
"name": "site_id",
"type": "uuid",
"primaryKey": false,
"notNull": true
},
"source": {
"name": "source",
"type": "text",
"primaryKey": false,
"notNull": false
},
"created_at": {
"name": "created_at",
"type": "timestamp",
"primaryKey": false,
"notNull": true,
"default": "now()"
}
},
"indexes": {},
"foreignKeys": {
"signups_site_id_sites_id_fk": {
"name": "signups_site_id_sites_id_fk",
"tableFrom": "signups",
"tableTo": "sites",
"columnsFrom": [
"site_id"
],
"columnsTo": [
"id"
],
"onDelete": "no action",
"onUpdate": "no action"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {}
},
"public.sites": {
"name": "sites",
"schema": "",
"columns": {
"id": {
"name": "id",
"type": "uuid",
"primaryKey": true,
"notNull": true,
"default": "gen_random_uuid()"
},
"user_id": {
"name": "user_id",
"type": "uuid",
"primaryKey": false,
"notNull": false
},
"name": {
"name": "name",
"type": "text",
"primaryKey": false,
"notNull": false
},
"primary_color_hex": {
"name": "primary_color_hex",
"type": "varchar(6)",
"primaryKey": false,
"notNull": true,
"default": "'c4ced4'"
},
"secondary_color_hex": {
"name": "secondary_color_hex",
"type": "varchar(6)",
"primaryKey": false,
"notNull": true,
"default": "'27251f'"
},
"text_color_hex": {
"name": "text_color_hex",
"type": "varchar(6)",
"primaryKey": false,
"notNull": true,
"default": "'ffffff'"
},
"title": {
"name": "title",
"type": "text",
"primaryKey": false,
"notNull": false,
"default": "'The best blog in the world!'"
},
"subtitle": {
"name": "subtitle",
"type": "text",
"primaryKey": false,
"notNull": false,
"default": "'Some extra info about the best blog in the world!'"
},
"domain": {
"name": "domain",
"type": "text",
"primaryKey": false,
"notNull": false,
"default": "NULL"
},
"send_freebie": {
"name": "send_freebie",
"type": "boolean",
"primaryKey": false,
"notNull": false,
"default": false
},
"freebie_name": {
"name": "freebie_name",
"type": "text",
"primaryKey": false,
"notNull": false,
"default": "''"
},
"freebie_url": {
"name": "freebie_url",
"type": "text",
"primaryKey": false,
"notNull": false,
"default": "''"
},
"freebie_text": {
"name": "freebie_text",
"type": "text",
"primaryKey": false,
"notNull": false,
"default": "''"
},
"freebie_image_url": {
"name": "freebie_image_url",
"type": "text",
"primaryKey": false,
"notNull": false,
"default": "''"
},
"subdomain_slug": {
"name": "subdomain_slug",
"type": "text",
"primaryKey": false,
"notNull": false
},
"social_medias": {
"name": "social_medias",
"type": "jsonb",
"primaryKey": false,
"notNull": false
}
},
"indexes": {},
"foreignKeys": {
"sites_user_id_users_id_fk": {
"name": "sites_user_id_users_id_fk",
"tableFrom": "sites",
"tableTo": "users",
"columnsFrom": [
"user_id"
],
"columnsTo": [
"id"
],
"onDelete": "no action",
"onUpdate": "no action"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {
"domainUniqueIndex": {
"name": "domainUniqueIndex",
"nullsNotDistinct": true,
"columns": [
"domain"
]
}
}
},
"public.users": {
"name": "users",
"schema": "",
"columns": {
"id": {
"name": "id",
"type": "uuid",
"primaryKey": true,
"notNull": true,
"default": "gen_random_uuid()"
},
"google_id": {
"name": "google_id",
"type": "text",
"primaryKey": false,
"notNull": false
},
"stripe_id": {
"name": "stripe_id",
"type": "text",
"primaryKey": false,
"notNull": false
},
"name": {
"name": "name",
"type": "text",
"primaryKey": false,
"notNull": false
},
"email": {
"name": "email",
"type": "text",
"primaryKey": false,
"notNull": false
},
"channel_id": {
"name": "channel_id",
"type": "text",
"primaryKey": false,
"notNull": false
},
"uploads_playlist_id": {
"name": "uploads_playlist_id",
"type": "text",
"primaryKey": false,
"notNull": false
},
"subscription_tier": {
"name": "subscription_tier",
"type": "subscription_tier",
"typeSchema": "public",
"primaryKey": false,
"notNull": true,
"default": "'free'"
},
"tokens": {
"name": "tokens",
"type": "integer",
"primaryKey": false,
"notNull": true,
"default": 0
}
},
"indexes": {},
"foreignKeys": {},
"compositePrimaryKeys": {},
"uniqueConstraints": {}
},
"public.waitlist": {
"name": "waitlist",
"schema": "",
"columns": {
"email": {
"name": "email",
"type": "text",
"primaryKey": false,
"notNull": false
}
},
"indexes": {},
"foreignKeys": {},
"compositePrimaryKeys": {},
"uniqueConstraints": {}
}
},
"enums": {
"public.subscription_tier": {
"name": "subscription_tier",
"schema": "public",
"values": [
"free",
"basic",
"pro",
"enterprise"
]
}
},
"schemas": {},
"_meta": {
"columns": {},
"schemas": {},
"tables": {}
}
}

View File

@ -1,454 +0,0 @@
{
"id": "3332ba7a-6f32-4ee9-8c5a-61dfbe163bda",
"prevId": "a8542e87-bade-4eae-b740-8ca9d8429118",
"version": "7",
"dialect": "postgresql",
"tables": {
"public.articles": {
"name": "articles",
"schema": "",
"columns": {
"id": {
"name": "id",
"type": "uuid",
"primaryKey": true,
"notNull": true,
"default": "gen_random_uuid()"
},
"site_id": {
"name": "site_id",
"type": "uuid",
"primaryKey": false,
"notNull": false
},
"content": {
"name": "content",
"type": "text",
"primaryKey": false,
"notNull": false
},
"source_video_id": {
"name": "source_video_id",
"type": "text",
"primaryKey": false,
"notNull": false
},
"title": {
"name": "title",
"type": "text",
"primaryKey": false,
"notNull": false
},
"seo_slug": {
"name": "seo_slug",
"type": "text",
"primaryKey": false,
"notNull": false
},
"is_public": {
"name": "is_public",
"type": "boolean",
"primaryKey": false,
"notNull": false,
"default": false
},
"views": {
"name": "views",
"type": "integer",
"primaryKey": false,
"notNull": false,
"default": 0
},
"seo_title": {
"name": "seo_title",
"type": "text",
"primaryKey": false,
"notNull": false
},
"seo_description": {
"name": "seo_description",
"type": "text",
"primaryKey": false,
"notNull": false
},
"excerp": {
"name": "excerp",
"type": "text",
"primaryKey": false,
"notNull": false
},
"created_at": {
"name": "created_at",
"type": "timestamp",
"primaryKey": false,
"notNull": false,
"default": "now()"
}
},
"indexes": {},
"foreignKeys": {
"articles_site_id_sites_id_fk": {
"name": "articles_site_id_sites_id_fk",
"tableFrom": "articles",
"tableTo": "sites",
"columnsFrom": [
"site_id"
],
"columnsTo": [
"id"
],
"onDelete": "no action",
"onUpdate": "no action"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {}
},
"public.sessions": {
"name": "sessions",
"schema": "",
"columns": {
"id": {
"name": "id",
"type": "uuid",
"primaryKey": true,
"notNull": true,
"default": "gen_random_uuid()"
},
"user_id": {
"name": "user_id",
"type": "uuid",
"primaryKey": false,
"notNull": false
},
"google_access_token": {
"name": "google_access_token",
"type": "text",
"primaryKey": false,
"notNull": false
},
"google_refresh_token": {
"name": "google_refresh_token",
"type": "text",
"primaryKey": false,
"notNull": false
},
"expires_at": {
"name": "expires_at",
"type": "timestamp",
"primaryKey": false,
"notNull": false
}
},
"indexes": {},
"foreignKeys": {
"sessions_user_id_users_id_fk": {
"name": "sessions_user_id_users_id_fk",
"tableFrom": "sessions",
"tableTo": "users",
"columnsFrom": [
"user_id"
],
"columnsTo": [
"id"
],
"onDelete": "no action",
"onUpdate": "no action"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {}
},
"public.signups": {
"name": "signups",
"schema": "",
"columns": {
"email": {
"name": "email",
"type": "text",
"primaryKey": false,
"notNull": true
},
"site_id": {
"name": "site_id",
"type": "uuid",
"primaryKey": false,
"notNull": true
},
"source": {
"name": "source",
"type": "text",
"primaryKey": false,
"notNull": false
},
"created_at": {
"name": "created_at",
"type": "timestamp",
"primaryKey": false,
"notNull": true,
"default": "now()"
}
},
"indexes": {},
"foreignKeys": {
"signups_site_id_sites_id_fk": {
"name": "signups_site_id_sites_id_fk",
"tableFrom": "signups",
"tableTo": "sites",
"columnsFrom": [
"site_id"
],
"columnsTo": [
"id"
],
"onDelete": "no action",
"onUpdate": "no action"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {}
},
"public.sites": {
"name": "sites",
"schema": "",
"columns": {
"id": {
"name": "id",
"type": "uuid",
"primaryKey": true,
"notNull": true,
"default": "gen_random_uuid()"
},
"user_id": {
"name": "user_id",
"type": "uuid",
"primaryKey": false,
"notNull": false
},
"name": {
"name": "name",
"type": "text",
"primaryKey": false,
"notNull": false
},
"primary_color_hex": {
"name": "primary_color_hex",
"type": "varchar(6)",
"primaryKey": false,
"notNull": true,
"default": "'c4ced4'"
},
"secondary_color_hex": {
"name": "secondary_color_hex",
"type": "varchar(6)",
"primaryKey": false,
"notNull": true,
"default": "'27251f'"
},
"text_color_hex": {
"name": "text_color_hex",
"type": "varchar(6)",
"primaryKey": false,
"notNull": true,
"default": "'ffffff'"
},
"title": {
"name": "title",
"type": "text",
"primaryKey": false,
"notNull": false,
"default": "'The best blog in the world!'"
},
"subtitle": {
"name": "subtitle",
"type": "text",
"primaryKey": false,
"notNull": false,
"default": "'Some extra info about the best blog in the world!'"
},
"domain": {
"name": "domain",
"type": "text",
"primaryKey": false,
"notNull": false,
"default": "NULL"
},
"send_freebie": {
"name": "send_freebie",
"type": "boolean",
"primaryKey": false,
"notNull": false,
"default": false
},
"freebie_name": {
"name": "freebie_name",
"type": "text",
"primaryKey": false,
"notNull": false,
"default": "''"
},
"freebie_url": {
"name": "freebie_url",
"type": "text",
"primaryKey": false,
"notNull": false,
"default": "''"
},
"freebie_text": {
"name": "freebie_text",
"type": "text",
"primaryKey": false,
"notNull": false,
"default": "''"
},
"freebie_image_url": {
"name": "freebie_image_url",
"type": "text",
"primaryKey": false,
"notNull": false,
"default": "''"
},
"subdomain_slug": {
"name": "subdomain_slug",
"type": "text",
"primaryKey": false,
"notNull": false
},
"social_medias": {
"name": "social_medias",
"type": "jsonb",
"primaryKey": false,
"notNull": false
}
},
"indexes": {},
"foreignKeys": {
"sites_user_id_users_id_fk": {
"name": "sites_user_id_users_id_fk",
"tableFrom": "sites",
"tableTo": "users",
"columnsFrom": [
"user_id"
],
"columnsTo": [
"id"
],
"onDelete": "no action",
"onUpdate": "no action"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {
"domainUniqueIndex": {
"name": "domainUniqueIndex",
"nullsNotDistinct": false,
"columns": [
"domain"
]
}
}
},
"public.users": {
"name": "users",
"schema": "",
"columns": {
"id": {
"name": "id",
"type": "uuid",
"primaryKey": true,
"notNull": true,
"default": "gen_random_uuid()"
},
"google_id": {
"name": "google_id",
"type": "text",
"primaryKey": false,
"notNull": false
},
"stripe_id": {
"name": "stripe_id",
"type": "text",
"primaryKey": false,
"notNull": false
},
"name": {
"name": "name",
"type": "text",
"primaryKey": false,
"notNull": false
},
"email": {
"name": "email",
"type": "text",
"primaryKey": false,
"notNull": false
},
"channel_id": {
"name": "channel_id",
"type": "text",
"primaryKey": false,
"notNull": false
},
"uploads_playlist_id": {
"name": "uploads_playlist_id",
"type": "text",
"primaryKey": false,
"notNull": false
},
"subscription_tier": {
"name": "subscription_tier",
"type": "subscription_tier",
"typeSchema": "public",
"primaryKey": false,
"notNull": true,
"default": "'free'"
},
"tokens": {
"name": "tokens",
"type": "integer",
"primaryKey": false,
"notNull": true,
"default": 0
}
},
"indexes": {},
"foreignKeys": {},
"compositePrimaryKeys": {},
"uniqueConstraints": {}
},
"public.waitlist": {
"name": "waitlist",
"schema": "",
"columns": {
"email": {
"name": "email",
"type": "text",
"primaryKey": false,
"notNull": false
}
},
"indexes": {},
"foreignKeys": {},
"compositePrimaryKeys": {},
"uniqueConstraints": {}
}
},
"enums": {
"public.subscription_tier": {
"name": "subscription_tier",
"schema": "public",
"values": [
"free",
"basic",
"pro",
"enterprise"
]
}
},
"schemas": {},
"_meta": {
"columns": {},
"schemas": {},
"tables": {}
}
}

View File

@ -1,454 +0,0 @@
{
"id": "e3391040-c733-41df-ab35-ddb8c0bbec80",
"prevId": "3332ba7a-6f32-4ee9-8c5a-61dfbe163bda",
"version": "7",
"dialect": "postgresql",
"tables": {
"public.articles": {
"name": "articles",
"schema": "",
"columns": {
"id": {
"name": "id",
"type": "uuid",
"primaryKey": true,
"notNull": true,
"default": "gen_random_uuid()"
},
"site_id": {
"name": "site_id",
"type": "uuid",
"primaryKey": false,
"notNull": false
},
"content": {
"name": "content",
"type": "text",
"primaryKey": false,
"notNull": false
},
"source_video_id": {
"name": "source_video_id",
"type": "text",
"primaryKey": false,
"notNull": false
},
"title": {
"name": "title",
"type": "text",
"primaryKey": false,
"notNull": false
},
"seo_slug": {
"name": "seo_slug",
"type": "text",
"primaryKey": false,
"notNull": false
},
"is_public": {
"name": "is_public",
"type": "boolean",
"primaryKey": false,
"notNull": false,
"default": false
},
"views": {
"name": "views",
"type": "integer",
"primaryKey": false,
"notNull": false,
"default": 0
},
"seo_title": {
"name": "seo_title",
"type": "text",
"primaryKey": false,
"notNull": false
},
"seo_description": {
"name": "seo_description",
"type": "text",
"primaryKey": false,
"notNull": false
},
"excerp": {
"name": "excerp",
"type": "text",
"primaryKey": false,
"notNull": false
},
"created_at": {
"name": "created_at",
"type": "timestamp",
"primaryKey": false,
"notNull": false,
"default": "now()"
}
},
"indexes": {},
"foreignKeys": {
"articles_site_id_sites_id_fk": {
"name": "articles_site_id_sites_id_fk",
"tableFrom": "articles",
"tableTo": "sites",
"columnsFrom": [
"site_id"
],
"columnsTo": [
"id"
],
"onDelete": "no action",
"onUpdate": "no action"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {}
},
"public.sessions": {
"name": "sessions",
"schema": "",
"columns": {
"id": {
"name": "id",
"type": "uuid",
"primaryKey": true,
"notNull": true,
"default": "gen_random_uuid()"
},
"user_id": {
"name": "user_id",
"type": "uuid",
"primaryKey": false,
"notNull": false
},
"google_access_token": {
"name": "google_access_token",
"type": "text",
"primaryKey": false,
"notNull": false
},
"google_refresh_token": {
"name": "google_refresh_token",
"type": "text",
"primaryKey": false,
"notNull": false
},
"expires_at": {
"name": "expires_at",
"type": "timestamp",
"primaryKey": false,
"notNull": false
}
},
"indexes": {},
"foreignKeys": {
"sessions_user_id_users_id_fk": {
"name": "sessions_user_id_users_id_fk",
"tableFrom": "sessions",
"tableTo": "users",
"columnsFrom": [
"user_id"
],
"columnsTo": [
"id"
],
"onDelete": "no action",
"onUpdate": "no action"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {}
},
"public.signups": {
"name": "signups",
"schema": "",
"columns": {
"email": {
"name": "email",
"type": "text",
"primaryKey": false,
"notNull": true
},
"site_id": {
"name": "site_id",
"type": "uuid",
"primaryKey": false,
"notNull": true
},
"source": {
"name": "source",
"type": "text",
"primaryKey": false,
"notNull": false
},
"created_at": {
"name": "created_at",
"type": "timestamp",
"primaryKey": false,
"notNull": true,
"default": "now()"
}
},
"indexes": {},
"foreignKeys": {
"signups_site_id_sites_id_fk": {
"name": "signups_site_id_sites_id_fk",
"tableFrom": "signups",
"tableTo": "sites",
"columnsFrom": [
"site_id"
],
"columnsTo": [
"id"
],
"onDelete": "no action",
"onUpdate": "no action"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {}
},
"public.sites": {
"name": "sites",
"schema": "",
"columns": {
"id": {
"name": "id",
"type": "uuid",
"primaryKey": true,
"notNull": true,
"default": "gen_random_uuid()"
},
"user_id": {
"name": "user_id",
"type": "uuid",
"primaryKey": false,
"notNull": false
},
"name": {
"name": "name",
"type": "text",
"primaryKey": false,
"notNull": false
},
"primary_color_hex": {
"name": "primary_color_hex",
"type": "varchar(6)",
"primaryKey": false,
"notNull": true,
"default": "'c4ced4'"
},
"secondary_color_hex": {
"name": "secondary_color_hex",
"type": "varchar(6)",
"primaryKey": false,
"notNull": true,
"default": "'27251f'"
},
"text_color_hex": {
"name": "text_color_hex",
"type": "varchar(6)",
"primaryKey": false,
"notNull": true,
"default": "'ffffff'"
},
"title": {
"name": "title",
"type": "text",
"primaryKey": false,
"notNull": false,
"default": "'The best blog in the world!'"
},
"subtitle": {
"name": "subtitle",
"type": "text",
"primaryKey": false,
"notNull": false,
"default": "'Some extra info about the best blog in the world!'"
},
"domain": {
"name": "domain",
"type": "text",
"primaryKey": false,
"notNull": false,
"default": "NULL"
},
"send_freebie": {
"name": "send_freebie",
"type": "boolean",
"primaryKey": false,
"notNull": false,
"default": false
},
"freebie_name": {
"name": "freebie_name",
"type": "text",
"primaryKey": false,
"notNull": false,
"default": "''"
},
"freebie_url": {
"name": "freebie_url",
"type": "text",
"primaryKey": false,
"notNull": false,
"default": "''"
},
"freebie_text": {
"name": "freebie_text",
"type": "text",
"primaryKey": false,
"notNull": false,
"default": "''"
},
"freebie_image_url": {
"name": "freebie_image_url",
"type": "text",
"primaryKey": false,
"notNull": false,
"default": "''"
},
"subdomain_slug": {
"name": "subdomain_slug",
"type": "text",
"primaryKey": false,
"notNull": false
},
"social_medias": {
"name": "social_medias",
"type": "jsonb",
"primaryKey": false,
"notNull": false
}
},
"indexes": {},
"foreignKeys": {
"sites_user_id_users_id_fk": {
"name": "sites_user_id_users_id_fk",
"tableFrom": "sites",
"tableTo": "users",
"columnsFrom": [
"user_id"
],
"columnsTo": [
"id"
],
"onDelete": "no action",
"onUpdate": "no action"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {
"sites_domain_unique": {
"name": "sites_domain_unique",
"nullsNotDistinct": false,
"columns": [
"domain"
]
}
}
},
"public.users": {
"name": "users",
"schema": "",
"columns": {
"id": {
"name": "id",
"type": "uuid",
"primaryKey": true,
"notNull": true,
"default": "gen_random_uuid()"
},
"google_id": {
"name": "google_id",
"type": "text",
"primaryKey": false,
"notNull": false
},
"stripe_id": {
"name": "stripe_id",
"type": "text",
"primaryKey": false,
"notNull": false
},
"name": {
"name": "name",
"type": "text",
"primaryKey": false,
"notNull": false
},
"email": {
"name": "email",
"type": "text",
"primaryKey": false,
"notNull": false
},
"channel_id": {
"name": "channel_id",
"type": "text",
"primaryKey": false,
"notNull": false
},
"uploads_playlist_id": {
"name": "uploads_playlist_id",
"type": "text",
"primaryKey": false,
"notNull": false
},
"subscription_tier": {
"name": "subscription_tier",
"type": "subscription_tier",
"typeSchema": "public",
"primaryKey": false,
"notNull": true,
"default": "'free'"
},
"tokens": {
"name": "tokens",
"type": "integer",
"primaryKey": false,
"notNull": true,
"default": 0
}
},
"indexes": {},
"foreignKeys": {},
"compositePrimaryKeys": {},
"uniqueConstraints": {}
},
"public.waitlist": {
"name": "waitlist",
"schema": "",
"columns": {
"email": {
"name": "email",
"type": "text",
"primaryKey": false,
"notNull": false
}
},
"indexes": {},
"foreignKeys": {},
"compositePrimaryKeys": {},
"uniqueConstraints": {}
}
},
"enums": {
"public.subscription_tier": {
"name": "subscription_tier",
"schema": "public",
"values": [
"free",
"basic",
"pro",
"enterprise"
]
}
},
"schemas": {},
"_meta": {
"columns": {},
"schemas": {},
"tables": {}
}
}

View File

@ -1,461 +0,0 @@
{
"id": "00d3bd2f-2c64-4026-982e-e67ce4a45a11",
"prevId": "e3391040-c733-41df-ab35-ddb8c0bbec80",
"version": "7",
"dialect": "postgresql",
"tables": {
"public.articles": {
"name": "articles",
"schema": "",
"columns": {
"id": {
"name": "id",
"type": "uuid",
"primaryKey": true,
"notNull": true,
"default": "gen_random_uuid()"
},
"site_id": {
"name": "site_id",
"type": "uuid",
"primaryKey": false,
"notNull": false
},
"content": {
"name": "content",
"type": "text",
"primaryKey": false,
"notNull": false
},
"source_video_id": {
"name": "source_video_id",
"type": "text",
"primaryKey": false,
"notNull": false
},
"title": {
"name": "title",
"type": "text",
"primaryKey": false,
"notNull": false
},
"seo_slug": {
"name": "seo_slug",
"type": "text",
"primaryKey": false,
"notNull": false
},
"is_public": {
"name": "is_public",
"type": "boolean",
"primaryKey": false,
"notNull": false,
"default": false
},
"views": {
"name": "views",
"type": "integer",
"primaryKey": false,
"notNull": false,
"default": 0
},
"seo_title": {
"name": "seo_title",
"type": "text",
"primaryKey": false,
"notNull": false
},
"seo_description": {
"name": "seo_description",
"type": "text",
"primaryKey": false,
"notNull": false
},
"excerp": {
"name": "excerp",
"type": "text",
"primaryKey": false,
"notNull": false
},
"created_at": {
"name": "created_at",
"type": "timestamp",
"primaryKey": false,
"notNull": false,
"default": "now()"
}
},
"indexes": {},
"foreignKeys": {
"articles_site_id_sites_id_fk": {
"name": "articles_site_id_sites_id_fk",
"tableFrom": "articles",
"tableTo": "sites",
"columnsFrom": [
"site_id"
],
"columnsTo": [
"id"
],
"onDelete": "no action",
"onUpdate": "no action"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {}
},
"public.sessions": {
"name": "sessions",
"schema": "",
"columns": {
"id": {
"name": "id",
"type": "uuid",
"primaryKey": true,
"notNull": true,
"default": "gen_random_uuid()"
},
"user_id": {
"name": "user_id",
"type": "uuid",
"primaryKey": false,
"notNull": false
},
"google_access_token": {
"name": "google_access_token",
"type": "text",
"primaryKey": false,
"notNull": false
},
"google_refresh_token": {
"name": "google_refresh_token",
"type": "text",
"primaryKey": false,
"notNull": false
},
"expires_at": {
"name": "expires_at",
"type": "timestamp",
"primaryKey": false,
"notNull": false
}
},
"indexes": {},
"foreignKeys": {
"sessions_user_id_users_id_fk": {
"name": "sessions_user_id_users_id_fk",
"tableFrom": "sessions",
"tableTo": "users",
"columnsFrom": [
"user_id"
],
"columnsTo": [
"id"
],
"onDelete": "no action",
"onUpdate": "no action"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {}
},
"public.signups": {
"name": "signups",
"schema": "",
"columns": {
"email": {
"name": "email",
"type": "text",
"primaryKey": false,
"notNull": true
},
"site_id": {
"name": "site_id",
"type": "uuid",
"primaryKey": false,
"notNull": true
},
"source": {
"name": "source",
"type": "text",
"primaryKey": false,
"notNull": false
},
"created_at": {
"name": "created_at",
"type": "timestamp",
"primaryKey": false,
"notNull": true,
"default": "now()"
}
},
"indexes": {},
"foreignKeys": {
"signups_site_id_sites_id_fk": {
"name": "signups_site_id_sites_id_fk",
"tableFrom": "signups",
"tableTo": "sites",
"columnsFrom": [
"site_id"
],
"columnsTo": [
"id"
],
"onDelete": "no action",
"onUpdate": "no action"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {}
},
"public.sites": {
"name": "sites",
"schema": "",
"columns": {
"id": {
"name": "id",
"type": "uuid",
"primaryKey": true,
"notNull": true,
"default": "gen_random_uuid()"
},
"user_id": {
"name": "user_id",
"type": "uuid",
"primaryKey": false,
"notNull": false
},
"name": {
"name": "name",
"type": "text",
"primaryKey": false,
"notNull": false
},
"primary_color_hex": {
"name": "primary_color_hex",
"type": "varchar(6)",
"primaryKey": false,
"notNull": true,
"default": "'c4ced4'"
},
"secondary_color_hex": {
"name": "secondary_color_hex",
"type": "varchar(6)",
"primaryKey": false,
"notNull": true,
"default": "'27251f'"
},
"text_color_hex": {
"name": "text_color_hex",
"type": "varchar(6)",
"primaryKey": false,
"notNull": true,
"default": "'ffffff'"
},
"title": {
"name": "title",
"type": "text",
"primaryKey": false,
"notNull": false,
"default": "'The best blog in the world!'"
},
"subtitle": {
"name": "subtitle",
"type": "text",
"primaryKey": false,
"notNull": false,
"default": "'Some extra info about the best blog in the world!'"
},
"domain": {
"name": "domain",
"type": "text",
"primaryKey": false,
"notNull": false,
"default": "NULL"
},
"send_freebie": {
"name": "send_freebie",
"type": "boolean",
"primaryKey": false,
"notNull": false,
"default": false
},
"freebie_name": {
"name": "freebie_name",
"type": "text",
"primaryKey": false,
"notNull": false,
"default": "''"
},
"freebie_url": {
"name": "freebie_url",
"type": "text",
"primaryKey": false,
"notNull": false,
"default": "''"
},
"freebie_text": {
"name": "freebie_text",
"type": "text",
"primaryKey": false,
"notNull": false,
"default": "''"
},
"freebie_image_url": {
"name": "freebie_image_url",
"type": "text",
"primaryKey": false,
"notNull": false,
"default": "''"
},
"subdomain_slug": {
"name": "subdomain_slug",
"type": "text",
"primaryKey": false,
"notNull": false
},
"social_medias": {
"name": "social_medias",
"type": "jsonb",
"primaryKey": false,
"notNull": false
},
"auto_publish": {
"name": "auto_publish",
"type": "boolean",
"primaryKey": false,
"notNull": false,
"default": false
}
},
"indexes": {},
"foreignKeys": {
"sites_user_id_users_id_fk": {
"name": "sites_user_id_users_id_fk",
"tableFrom": "sites",
"tableTo": "users",
"columnsFrom": [
"user_id"
],
"columnsTo": [
"id"
],
"onDelete": "no action",
"onUpdate": "no action"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {
"sites_domain_unique": {
"name": "sites_domain_unique",
"nullsNotDistinct": false,
"columns": [
"domain"
]
}
}
},
"public.users": {
"name": "users",
"schema": "",
"columns": {
"id": {
"name": "id",
"type": "uuid",
"primaryKey": true,
"notNull": true,
"default": "gen_random_uuid()"
},
"google_id": {
"name": "google_id",
"type": "text",
"primaryKey": false,
"notNull": false
},
"stripe_id": {
"name": "stripe_id",
"type": "text",
"primaryKey": false,
"notNull": false
},
"name": {
"name": "name",
"type": "text",
"primaryKey": false,
"notNull": false
},
"email": {
"name": "email",
"type": "text",
"primaryKey": false,
"notNull": false
},
"channel_id": {
"name": "channel_id",
"type": "text",
"primaryKey": false,
"notNull": false
},
"uploads_playlist_id": {
"name": "uploads_playlist_id",
"type": "text",
"primaryKey": false,
"notNull": false
},
"subscription_tier": {
"name": "subscription_tier",
"type": "subscription_tier",
"typeSchema": "public",
"primaryKey": false,
"notNull": true,
"default": "'free'"
},
"tokens": {
"name": "tokens",
"type": "integer",
"primaryKey": false,
"notNull": true,
"default": 200
}
},
"indexes": {},
"foreignKeys": {},
"compositePrimaryKeys": {},
"uniqueConstraints": {}
},
"public.waitlist": {
"name": "waitlist",
"schema": "",
"columns": {
"email": {
"name": "email",
"type": "text",
"primaryKey": false,
"notNull": false
}
},
"indexes": {},
"foreignKeys": {},
"compositePrimaryKeys": {},
"uniqueConstraints": {}
}
},
"enums": {
"public.subscription_tier": {
"name": "subscription_tier",
"schema": "public",
"values": [
"free",
"basic",
"pro",
"enterprise"
]
}
},
"schemas": {},
"_meta": {
"columns": {},
"schemas": {},
"tables": {}
}
}

View File

@ -1,467 +0,0 @@
{
"id": "77f7612a-c9c8-4843-b0b6-09842cd9cff7",
"prevId": "00d3bd2f-2c64-4026-982e-e67ce4a45a11",
"version": "7",
"dialect": "postgresql",
"tables": {
"public.articles": {
"name": "articles",
"schema": "",
"columns": {
"id": {
"name": "id",
"type": "uuid",
"primaryKey": true,
"notNull": true,
"default": "gen_random_uuid()"
},
"site_id": {
"name": "site_id",
"type": "uuid",
"primaryKey": false,
"notNull": false
},
"content": {
"name": "content",
"type": "text",
"primaryKey": false,
"notNull": false
},
"source_video_id": {
"name": "source_video_id",
"type": "text",
"primaryKey": false,
"notNull": false
},
"title": {
"name": "title",
"type": "text",
"primaryKey": false,
"notNull": false
},
"seo_slug": {
"name": "seo_slug",
"type": "text",
"primaryKey": false,
"notNull": false
},
"is_public": {
"name": "is_public",
"type": "boolean",
"primaryKey": false,
"notNull": false,
"default": false
},
"views": {
"name": "views",
"type": "integer",
"primaryKey": false,
"notNull": false,
"default": 0
},
"seo_title": {
"name": "seo_title",
"type": "text",
"primaryKey": false,
"notNull": false
},
"seo_description": {
"name": "seo_description",
"type": "text",
"primaryKey": false,
"notNull": false
},
"excerp": {
"name": "excerp",
"type": "text",
"primaryKey": false,
"notNull": false
},
"created_at": {
"name": "created_at",
"type": "timestamp",
"primaryKey": false,
"notNull": false,
"default": "now()"
}
},
"indexes": {},
"foreignKeys": {
"articles_site_id_sites_id_fk": {
"name": "articles_site_id_sites_id_fk",
"tableFrom": "articles",
"tableTo": "sites",
"columnsFrom": [
"site_id"
],
"columnsTo": [
"id"
],
"onDelete": "no action",
"onUpdate": "no action"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {}
},
"public.sessions": {
"name": "sessions",
"schema": "",
"columns": {
"id": {
"name": "id",
"type": "uuid",
"primaryKey": true,
"notNull": true,
"default": "gen_random_uuid()"
},
"user_id": {
"name": "user_id",
"type": "uuid",
"primaryKey": false,
"notNull": false
},
"google_access_token": {
"name": "google_access_token",
"type": "text",
"primaryKey": false,
"notNull": false
},
"google_refresh_token": {
"name": "google_refresh_token",
"type": "text",
"primaryKey": false,
"notNull": false
},
"expires_at": {
"name": "expires_at",
"type": "timestamp",
"primaryKey": false,
"notNull": false
}
},
"indexes": {},
"foreignKeys": {
"sessions_user_id_users_id_fk": {
"name": "sessions_user_id_users_id_fk",
"tableFrom": "sessions",
"tableTo": "users",
"columnsFrom": [
"user_id"
],
"columnsTo": [
"id"
],
"onDelete": "no action",
"onUpdate": "no action"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {}
},
"public.signups": {
"name": "signups",
"schema": "",
"columns": {
"email": {
"name": "email",
"type": "text",
"primaryKey": false,
"notNull": true
},
"site_id": {
"name": "site_id",
"type": "uuid",
"primaryKey": false,
"notNull": true
},
"source": {
"name": "source",
"type": "text",
"primaryKey": false,
"notNull": false
},
"created_at": {
"name": "created_at",
"type": "timestamp",
"primaryKey": false,
"notNull": true,
"default": "now()"
}
},
"indexes": {},
"foreignKeys": {
"signups_site_id_sites_id_fk": {
"name": "signups_site_id_sites_id_fk",
"tableFrom": "signups",
"tableTo": "sites",
"columnsFrom": [
"site_id"
],
"columnsTo": [
"id"
],
"onDelete": "no action",
"onUpdate": "no action"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {}
},
"public.sites": {
"name": "sites",
"schema": "",
"columns": {
"id": {
"name": "id",
"type": "uuid",
"primaryKey": true,
"notNull": true,
"default": "gen_random_uuid()"
},
"user_id": {
"name": "user_id",
"type": "uuid",
"primaryKey": false,
"notNull": false
},
"name": {
"name": "name",
"type": "text",
"primaryKey": false,
"notNull": false
},
"primary_color_hex": {
"name": "primary_color_hex",
"type": "varchar(6)",
"primaryKey": false,
"notNull": true,
"default": "'c4ced4'"
},
"secondary_color_hex": {
"name": "secondary_color_hex",
"type": "varchar(6)",
"primaryKey": false,
"notNull": true,
"default": "'27251f'"
},
"text_color_hex": {
"name": "text_color_hex",
"type": "varchar(6)",
"primaryKey": false,
"notNull": true,
"default": "'ffffff'"
},
"title": {
"name": "title",
"type": "text",
"primaryKey": false,
"notNull": false,
"default": "'The best blog in the world!'"
},
"subtitle": {
"name": "subtitle",
"type": "text",
"primaryKey": false,
"notNull": false,
"default": "'Some extra info about the best blog in the world!'"
},
"domain": {
"name": "domain",
"type": "text",
"primaryKey": false,
"notNull": false,
"default": "NULL"
},
"send_freebie": {
"name": "send_freebie",
"type": "boolean",
"primaryKey": false,
"notNull": false,
"default": false
},
"freebie_name": {
"name": "freebie_name",
"type": "text",
"primaryKey": false,
"notNull": false,
"default": "''"
},
"freebie_url": {
"name": "freebie_url",
"type": "text",
"primaryKey": false,
"notNull": false,
"default": "''"
},
"freebie_text": {
"name": "freebie_text",
"type": "text",
"primaryKey": false,
"notNull": false,
"default": "''"
},
"freebie_image_url": {
"name": "freebie_image_url",
"type": "text",
"primaryKey": false,
"notNull": false,
"default": "''"
},
"subdomain_slug": {
"name": "subdomain_slug",
"type": "text",
"primaryKey": false,
"notNull": false
},
"social_medias": {
"name": "social_medias",
"type": "jsonb",
"primaryKey": false,
"notNull": false
},
"auto_publish": {
"name": "auto_publish",
"type": "boolean",
"primaryKey": false,
"notNull": false,
"default": false
},
"pubsub_expiry": {
"name": "pubsub_expiry",
"type": "date",
"primaryKey": false,
"notNull": false
}
},
"indexes": {},
"foreignKeys": {
"sites_user_id_users_id_fk": {
"name": "sites_user_id_users_id_fk",
"tableFrom": "sites",
"tableTo": "users",
"columnsFrom": [
"user_id"
],
"columnsTo": [
"id"
],
"onDelete": "no action",
"onUpdate": "no action"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {
"sites_domain_unique": {
"name": "sites_domain_unique",
"nullsNotDistinct": false,
"columns": [
"domain"
]
}
}
},
"public.users": {
"name": "users",
"schema": "",
"columns": {
"id": {
"name": "id",
"type": "uuid",
"primaryKey": true,
"notNull": true,
"default": "gen_random_uuid()"
},
"google_id": {
"name": "google_id",
"type": "text",
"primaryKey": false,
"notNull": false
},
"stripe_id": {
"name": "stripe_id",
"type": "text",
"primaryKey": false,
"notNull": false
},
"name": {
"name": "name",
"type": "text",
"primaryKey": false,
"notNull": false
},
"email": {
"name": "email",
"type": "text",
"primaryKey": false,
"notNull": false
},
"channel_id": {
"name": "channel_id",
"type": "text",
"primaryKey": false,
"notNull": false
},
"uploads_playlist_id": {
"name": "uploads_playlist_id",
"type": "text",
"primaryKey": false,
"notNull": false
},
"subscription_tier": {
"name": "subscription_tier",
"type": "subscription_tier",
"typeSchema": "public",
"primaryKey": false,
"notNull": true,
"default": "'free'"
},
"tokens": {
"name": "tokens",
"type": "integer",
"primaryKey": false,
"notNull": true,
"default": 200
}
},
"indexes": {},
"foreignKeys": {},
"compositePrimaryKeys": {},
"uniqueConstraints": {}
},
"public.waitlist": {
"name": "waitlist",
"schema": "",
"columns": {
"email": {
"name": "email",
"type": "text",
"primaryKey": false,
"notNull": false
}
},
"indexes": {},
"foreignKeys": {},
"compositePrimaryKeys": {},
"uniqueConstraints": {}
}
},
"enums": {
"public.subscription_tier": {
"name": "subscription_tier",
"schema": "public",
"values": [
"free",
"basic",
"pro",
"enterprise"
]
}
},
"schemas": {},
"_meta": {
"columns": {},
"schemas": {},
"tables": {}
}
}

View File

@ -1,467 +0,0 @@
{
"id": "d4c8325d-683a-4d6d-bcf7-bf16892332cc",
"prevId": "77f7612a-c9c8-4843-b0b6-09842cd9cff7",
"version": "7",
"dialect": "postgresql",
"tables": {
"public.articles": {
"name": "articles",
"schema": "",
"columns": {
"id": {
"name": "id",
"type": "uuid",
"primaryKey": true,
"notNull": true,
"default": "gen_random_uuid()"
},
"site_id": {
"name": "site_id",
"type": "uuid",
"primaryKey": false,
"notNull": false
},
"content": {
"name": "content",
"type": "text",
"primaryKey": false,
"notNull": false
},
"source_video_id": {
"name": "source_video_id",
"type": "text",
"primaryKey": false,
"notNull": false
},
"title": {
"name": "title",
"type": "text",
"primaryKey": false,
"notNull": false
},
"seo_slug": {
"name": "seo_slug",
"type": "text",
"primaryKey": false,
"notNull": false
},
"is_public": {
"name": "is_public",
"type": "boolean",
"primaryKey": false,
"notNull": false,
"default": false
},
"views": {
"name": "views",
"type": "integer",
"primaryKey": false,
"notNull": false,
"default": 0
},
"seo_title": {
"name": "seo_title",
"type": "text",
"primaryKey": false,
"notNull": false
},
"seo_description": {
"name": "seo_description",
"type": "text",
"primaryKey": false,
"notNull": false
},
"excerp": {
"name": "excerp",
"type": "text",
"primaryKey": false,
"notNull": false
},
"created_at": {
"name": "created_at",
"type": "timestamp",
"primaryKey": false,
"notNull": false,
"default": "now()"
}
},
"indexes": {},
"foreignKeys": {
"articles_site_id_sites_id_fk": {
"name": "articles_site_id_sites_id_fk",
"tableFrom": "articles",
"tableTo": "sites",
"columnsFrom": [
"site_id"
],
"columnsTo": [
"id"
],
"onDelete": "no action",
"onUpdate": "no action"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {}
},
"public.sessions": {
"name": "sessions",
"schema": "",
"columns": {
"id": {
"name": "id",
"type": "uuid",
"primaryKey": true,
"notNull": true,
"default": "gen_random_uuid()"
},
"user_id": {
"name": "user_id",
"type": "uuid",
"primaryKey": false,
"notNull": false
},
"google_access_token": {
"name": "google_access_token",
"type": "text",
"primaryKey": false,
"notNull": false
},
"google_refresh_token": {
"name": "google_refresh_token",
"type": "text",
"primaryKey": false,
"notNull": false
},
"expires_at": {
"name": "expires_at",
"type": "timestamp",
"primaryKey": false,
"notNull": false
}
},
"indexes": {},
"foreignKeys": {
"sessions_user_id_users_id_fk": {
"name": "sessions_user_id_users_id_fk",
"tableFrom": "sessions",
"tableTo": "users",
"columnsFrom": [
"user_id"
],
"columnsTo": [
"id"
],
"onDelete": "no action",
"onUpdate": "no action"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {}
},
"public.signups": {
"name": "signups",
"schema": "",
"columns": {
"email": {
"name": "email",
"type": "text",
"primaryKey": false,
"notNull": true
},
"site_id": {
"name": "site_id",
"type": "uuid",
"primaryKey": false,
"notNull": true
},
"source": {
"name": "source",
"type": "text",
"primaryKey": false,
"notNull": false
},
"created_at": {
"name": "created_at",
"type": "timestamp",
"primaryKey": false,
"notNull": true,
"default": "now()"
}
},
"indexes": {},
"foreignKeys": {
"signups_site_id_sites_id_fk": {
"name": "signups_site_id_sites_id_fk",
"tableFrom": "signups",
"tableTo": "sites",
"columnsFrom": [
"site_id"
],
"columnsTo": [
"id"
],
"onDelete": "no action",
"onUpdate": "no action"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {}
},
"public.sites": {
"name": "sites",
"schema": "",
"columns": {
"id": {
"name": "id",
"type": "uuid",
"primaryKey": true,
"notNull": true,
"default": "gen_random_uuid()"
},
"user_id": {
"name": "user_id",
"type": "uuid",
"primaryKey": false,
"notNull": false
},
"name": {
"name": "name",
"type": "text",
"primaryKey": false,
"notNull": false
},
"primary_color_hex": {
"name": "primary_color_hex",
"type": "varchar(6)",
"primaryKey": false,
"notNull": true,
"default": "'c4ced4'"
},
"secondary_color_hex": {
"name": "secondary_color_hex",
"type": "varchar(6)",
"primaryKey": false,
"notNull": true,
"default": "'27251f'"
},
"text_color_hex": {
"name": "text_color_hex",
"type": "varchar(6)",
"primaryKey": false,
"notNull": true,
"default": "'ffffff'"
},
"title": {
"name": "title",
"type": "text",
"primaryKey": false,
"notNull": false,
"default": "'The best blog in the world!'"
},
"subtitle": {
"name": "subtitle",
"type": "text",
"primaryKey": false,
"notNull": false,
"default": "'Some extra info about the best blog in the world!'"
},
"domain": {
"name": "domain",
"type": "text",
"primaryKey": false,
"notNull": false,
"default": "NULL"
},
"send_freebie": {
"name": "send_freebie",
"type": "boolean",
"primaryKey": false,
"notNull": false,
"default": false
},
"freebie_name": {
"name": "freebie_name",
"type": "text",
"primaryKey": false,
"notNull": false,
"default": "''"
},
"freebie_url": {
"name": "freebie_url",
"type": "text",
"primaryKey": false,
"notNull": false,
"default": "''"
},
"freebie_text": {
"name": "freebie_text",
"type": "text",
"primaryKey": false,
"notNull": false,
"default": "''"
},
"freebie_image_url": {
"name": "freebie_image_url",
"type": "text",
"primaryKey": false,
"notNull": false,
"default": "''"
},
"subdomain_slug": {
"name": "subdomain_slug",
"type": "text",
"primaryKey": false,
"notNull": false
},
"social_medias": {
"name": "social_medias",
"type": "jsonb",
"primaryKey": false,
"notNull": false
},
"auto_publish": {
"name": "auto_publish",
"type": "boolean",
"primaryKey": false,
"notNull": false,
"default": false
},
"pubsub_expiry": {
"name": "pubsub_expiry",
"type": "timestamp",
"primaryKey": false,
"notNull": false
}
},
"indexes": {},
"foreignKeys": {
"sites_user_id_users_id_fk": {
"name": "sites_user_id_users_id_fk",
"tableFrom": "sites",
"tableTo": "users",
"columnsFrom": [
"user_id"
],
"columnsTo": [
"id"
],
"onDelete": "no action",
"onUpdate": "no action"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {
"sites_domain_unique": {
"name": "sites_domain_unique",
"nullsNotDistinct": false,
"columns": [
"domain"
]
}
}
},
"public.users": {
"name": "users",
"schema": "",
"columns": {
"id": {
"name": "id",
"type": "uuid",
"primaryKey": true,
"notNull": true,
"default": "gen_random_uuid()"
},
"google_id": {
"name": "google_id",
"type": "text",
"primaryKey": false,
"notNull": false
},
"stripe_id": {
"name": "stripe_id",
"type": "text",
"primaryKey": false,
"notNull": false
},
"name": {
"name": "name",
"type": "text",
"primaryKey": false,
"notNull": false
},
"email": {
"name": "email",
"type": "text",
"primaryKey": false,
"notNull": false
},
"channel_id": {
"name": "channel_id",
"type": "text",
"primaryKey": false,
"notNull": false
},
"uploads_playlist_id": {
"name": "uploads_playlist_id",
"type": "text",
"primaryKey": false,
"notNull": false
},
"subscription_tier": {
"name": "subscription_tier",
"type": "subscription_tier",
"typeSchema": "public",
"primaryKey": false,
"notNull": true,
"default": "'free'"
},
"tokens": {
"name": "tokens",
"type": "integer",
"primaryKey": false,
"notNull": true,
"default": 200
}
},
"indexes": {},
"foreignKeys": {},
"compositePrimaryKeys": {},
"uniqueConstraints": {}
},
"public.waitlist": {
"name": "waitlist",
"schema": "",
"columns": {
"email": {
"name": "email",
"type": "text",
"primaryKey": false,
"notNull": false
}
},
"indexes": {},
"foreignKeys": {},
"compositePrimaryKeys": {},
"uniqueConstraints": {}
}
},
"enums": {
"public.subscription_tier": {
"name": "subscription_tier",
"schema": "public",
"values": [
"free",
"basic",
"pro",
"enterprise"
]
}
},
"schemas": {},
"_meta": {
"columns": {},
"schemas": {},
"tables": {}
}
}

View File

@ -1,485 +0,0 @@
{
"id": "805686b7-a39a-4dd8-b119-37d73e86d686",
"prevId": "d4c8325d-683a-4d6d-bcf7-bf16892332cc",
"version": "7",
"dialect": "postgresql",
"tables": {
"public.articles": {
"name": "articles",
"schema": "",
"columns": {
"id": {
"name": "id",
"type": "uuid",
"primaryKey": true,
"notNull": true,
"default": "gen_random_uuid()"
},
"site_id": {
"name": "site_id",
"type": "uuid",
"primaryKey": false,
"notNull": false
},
"content": {
"name": "content",
"type": "text",
"primaryKey": false,
"notNull": false
},
"source_video_id": {
"name": "source_video_id",
"type": "text",
"primaryKey": false,
"notNull": false
},
"title": {
"name": "title",
"type": "text",
"primaryKey": false,
"notNull": false
},
"seo_slug": {
"name": "seo_slug",
"type": "text",
"primaryKey": false,
"notNull": false
},
"is_public": {
"name": "is_public",
"type": "boolean",
"primaryKey": false,
"notNull": false,
"default": false
},
"views": {
"name": "views",
"type": "integer",
"primaryKey": false,
"notNull": false,
"default": 0
},
"seo_title": {
"name": "seo_title",
"type": "text",
"primaryKey": false,
"notNull": false
},
"seo_description": {
"name": "seo_description",
"type": "text",
"primaryKey": false,
"notNull": false
},
"excerp": {
"name": "excerp",
"type": "text",
"primaryKey": false,
"notNull": false
},
"created_at": {
"name": "created_at",
"type": "timestamp",
"primaryKey": false,
"notNull": false,
"default": "now()"
},
"status": {
"name": "status",
"type": "article_status",
"typeSchema": "public",
"primaryKey": false,
"notNull": false,
"default": "'done'"
}
},
"indexes": {},
"foreignKeys": {
"articles_site_id_sites_id_fk": {
"name": "articles_site_id_sites_id_fk",
"tableFrom": "articles",
"tableTo": "sites",
"columnsFrom": [
"site_id"
],
"columnsTo": [
"id"
],
"onDelete": "no action",
"onUpdate": "no action"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {}
},
"public.sessions": {
"name": "sessions",
"schema": "",
"columns": {
"id": {
"name": "id",
"type": "uuid",
"primaryKey": true,
"notNull": true,
"default": "gen_random_uuid()"
},
"user_id": {
"name": "user_id",
"type": "uuid",
"primaryKey": false,
"notNull": false
},
"google_access_token": {
"name": "google_access_token",
"type": "text",
"primaryKey": false,
"notNull": false
},
"google_refresh_token": {
"name": "google_refresh_token",
"type": "text",
"primaryKey": false,
"notNull": false
},
"expires_at": {
"name": "expires_at",
"type": "timestamp",
"primaryKey": false,
"notNull": false
}
},
"indexes": {},
"foreignKeys": {
"sessions_user_id_users_id_fk": {
"name": "sessions_user_id_users_id_fk",
"tableFrom": "sessions",
"tableTo": "users",
"columnsFrom": [
"user_id"
],
"columnsTo": [
"id"
],
"onDelete": "no action",
"onUpdate": "no action"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {}
},
"public.signups": {
"name": "signups",
"schema": "",
"columns": {
"email": {
"name": "email",
"type": "text",
"primaryKey": false,
"notNull": true
},
"site_id": {
"name": "site_id",
"type": "uuid",
"primaryKey": false,
"notNull": true
},
"source": {
"name": "source",
"type": "text",
"primaryKey": false,
"notNull": false
},
"created_at": {
"name": "created_at",
"type": "timestamp",
"primaryKey": false,
"notNull": true,
"default": "now()"
}
},
"indexes": {},
"foreignKeys": {
"signups_site_id_sites_id_fk": {
"name": "signups_site_id_sites_id_fk",
"tableFrom": "signups",
"tableTo": "sites",
"columnsFrom": [
"site_id"
],
"columnsTo": [
"id"
],
"onDelete": "no action",
"onUpdate": "no action"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {}
},
"public.sites": {
"name": "sites",
"schema": "",
"columns": {
"id": {
"name": "id",
"type": "uuid",
"primaryKey": true,
"notNull": true,
"default": "gen_random_uuid()"
},
"user_id": {
"name": "user_id",
"type": "uuid",
"primaryKey": false,
"notNull": false
},
"name": {
"name": "name",
"type": "text",
"primaryKey": false,
"notNull": false
},
"primary_color_hex": {
"name": "primary_color_hex",
"type": "varchar(6)",
"primaryKey": false,
"notNull": true,
"default": "'c4ced4'"
},
"secondary_color_hex": {
"name": "secondary_color_hex",
"type": "varchar(6)",
"primaryKey": false,
"notNull": true,
"default": "'27251f'"
},
"text_color_hex": {
"name": "text_color_hex",
"type": "varchar(6)",
"primaryKey": false,
"notNull": true,
"default": "'ffffff'"
},
"title": {
"name": "title",
"type": "text",
"primaryKey": false,
"notNull": false,
"default": "'The best blog in the world!'"
},
"subtitle": {
"name": "subtitle",
"type": "text",
"primaryKey": false,
"notNull": false,
"default": "'Some extra info about the best blog in the world!'"
},
"domain": {
"name": "domain",
"type": "text",
"primaryKey": false,
"notNull": false,
"default": "NULL"
},
"send_freebie": {
"name": "send_freebie",
"type": "boolean",
"primaryKey": false,
"notNull": false,
"default": false
},
"freebie_name": {
"name": "freebie_name",
"type": "text",
"primaryKey": false,
"notNull": false,
"default": "''"
},
"freebie_url": {
"name": "freebie_url",
"type": "text",
"primaryKey": false,
"notNull": false,
"default": "''"
},
"freebie_text": {
"name": "freebie_text",
"type": "text",
"primaryKey": false,
"notNull": false,
"default": "''"
},
"freebie_image_url": {
"name": "freebie_image_url",
"type": "text",
"primaryKey": false,
"notNull": false,
"default": "''"
},
"subdomain_slug": {
"name": "subdomain_slug",
"type": "text",
"primaryKey": false,
"notNull": false
},
"social_medias": {
"name": "social_medias",
"type": "jsonb",
"primaryKey": false,
"notNull": false
},
"auto_publish": {
"name": "auto_publish",
"type": "boolean",
"primaryKey": false,
"notNull": false,
"default": false
},
"pubsub_expiry": {
"name": "pubsub_expiry",
"type": "timestamp",
"primaryKey": false,
"notNull": false
}
},
"indexes": {},
"foreignKeys": {
"sites_user_id_users_id_fk": {
"name": "sites_user_id_users_id_fk",
"tableFrom": "sites",
"tableTo": "users",
"columnsFrom": [
"user_id"
],
"columnsTo": [
"id"
],
"onDelete": "no action",
"onUpdate": "no action"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {
"sites_domain_unique": {
"name": "sites_domain_unique",
"nullsNotDistinct": false,
"columns": [
"domain"
]
}
}
},
"public.users": {
"name": "users",
"schema": "",
"columns": {
"id": {
"name": "id",
"type": "uuid",
"primaryKey": true,
"notNull": true,
"default": "gen_random_uuid()"
},
"google_id": {
"name": "google_id",
"type": "text",
"primaryKey": false,
"notNull": false
},
"stripe_id": {
"name": "stripe_id",
"type": "text",
"primaryKey": false,
"notNull": false
},
"name": {
"name": "name",
"type": "text",
"primaryKey": false,
"notNull": false
},
"email": {
"name": "email",
"type": "text",
"primaryKey": false,
"notNull": false
},
"channel_id": {
"name": "channel_id",
"type": "text",
"primaryKey": false,
"notNull": false
},
"uploads_playlist_id": {
"name": "uploads_playlist_id",
"type": "text",
"primaryKey": false,
"notNull": false
},
"subscription_tier": {
"name": "subscription_tier",
"type": "subscription_tier",
"typeSchema": "public",
"primaryKey": false,
"notNull": true,
"default": "'free'"
},
"tokens": {
"name": "tokens",
"type": "integer",
"primaryKey": false,
"notNull": true,
"default": 200
}
},
"indexes": {},
"foreignKeys": {},
"compositePrimaryKeys": {},
"uniqueConstraints": {}
},
"public.waitlist": {
"name": "waitlist",
"schema": "",
"columns": {
"email": {
"name": "email",
"type": "text",
"primaryKey": false,
"notNull": false
}
},
"indexes": {},
"foreignKeys": {},
"compositePrimaryKeys": {},
"uniqueConstraints": {}
}
},
"enums": {
"public.article_status": {
"name": "article_status",
"schema": "public",
"values": [
"queued",
"transcribing",
"generating",
"done"
]
},
"public.subscription_tier": {
"name": "subscription_tier",
"schema": "public",
"values": [
"free",
"basic",
"pro",
"enterprise"
]
}
},
"schemas": {},
"_meta": {
"columns": {},
"schemas": {},
"tables": {}
}
}

View File

@ -1,485 +0,0 @@
{
"id": "9e95f80e-79e7-4af0-9a8e-327fb358ec26",
"prevId": "805686b7-a39a-4dd8-b119-37d73e86d686",
"version": "7",
"dialect": "postgresql",
"tables": {
"public.articles": {
"name": "articles",
"schema": "",
"columns": {
"id": {
"name": "id",
"type": "uuid",
"primaryKey": true,
"notNull": true,
"default": "gen_random_uuid()"
},
"site_id": {
"name": "site_id",
"type": "uuid",
"primaryKey": false,
"notNull": false
},
"content": {
"name": "content",
"type": "text",
"primaryKey": false,
"notNull": false
},
"source_video_id": {
"name": "source_video_id",
"type": "text",
"primaryKey": false,
"notNull": false
},
"title": {
"name": "title",
"type": "text",
"primaryKey": false,
"notNull": false
},
"seo_slug": {
"name": "seo_slug",
"type": "text",
"primaryKey": false,
"notNull": false
},
"is_public": {
"name": "is_public",
"type": "boolean",
"primaryKey": false,
"notNull": false,
"default": false
},
"views": {
"name": "views",
"type": "integer",
"primaryKey": false,
"notNull": false,
"default": 0
},
"seo_title": {
"name": "seo_title",
"type": "text",
"primaryKey": false,
"notNull": false
},
"seo_description": {
"name": "seo_description",
"type": "text",
"primaryKey": false,
"notNull": false
},
"excerp": {
"name": "excerp",
"type": "text",
"primaryKey": false,
"notNull": false
},
"created_at": {
"name": "created_at",
"type": "timestamp",
"primaryKey": false,
"notNull": false,
"default": "now()"
},
"status": {
"name": "status",
"type": "article_status",
"typeSchema": "public",
"primaryKey": false,
"notNull": false,
"default": "'queued'"
}
},
"indexes": {},
"foreignKeys": {
"articles_site_id_sites_id_fk": {
"name": "articles_site_id_sites_id_fk",
"tableFrom": "articles",
"tableTo": "sites",
"columnsFrom": [
"site_id"
],
"columnsTo": [
"id"
],
"onDelete": "no action",
"onUpdate": "no action"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {}
},
"public.sessions": {
"name": "sessions",
"schema": "",
"columns": {
"id": {
"name": "id",
"type": "uuid",
"primaryKey": true,
"notNull": true,
"default": "gen_random_uuid()"
},
"user_id": {
"name": "user_id",
"type": "uuid",
"primaryKey": false,
"notNull": false
},
"google_access_token": {
"name": "google_access_token",
"type": "text",
"primaryKey": false,
"notNull": false
},
"google_refresh_token": {
"name": "google_refresh_token",
"type": "text",
"primaryKey": false,
"notNull": false
},
"expires_at": {
"name": "expires_at",
"type": "timestamp",
"primaryKey": false,
"notNull": false
}
},
"indexes": {},
"foreignKeys": {
"sessions_user_id_users_id_fk": {
"name": "sessions_user_id_users_id_fk",
"tableFrom": "sessions",
"tableTo": "users",
"columnsFrom": [
"user_id"
],
"columnsTo": [
"id"
],
"onDelete": "no action",
"onUpdate": "no action"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {}
},
"public.signups": {
"name": "signups",
"schema": "",
"columns": {
"email": {
"name": "email",
"type": "text",
"primaryKey": false,
"notNull": true
},
"site_id": {
"name": "site_id",
"type": "uuid",
"primaryKey": false,
"notNull": true
},
"source": {
"name": "source",
"type": "text",
"primaryKey": false,
"notNull": false
},
"created_at": {
"name": "created_at",
"type": "timestamp",
"primaryKey": false,
"notNull": true,
"default": "now()"
}
},
"indexes": {},
"foreignKeys": {
"signups_site_id_sites_id_fk": {
"name": "signups_site_id_sites_id_fk",
"tableFrom": "signups",
"tableTo": "sites",
"columnsFrom": [
"site_id"
],
"columnsTo": [
"id"
],
"onDelete": "no action",
"onUpdate": "no action"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {}
},
"public.sites": {
"name": "sites",
"schema": "",
"columns": {
"id": {
"name": "id",
"type": "uuid",
"primaryKey": true,
"notNull": true,
"default": "gen_random_uuid()"
},
"user_id": {
"name": "user_id",
"type": "uuid",
"primaryKey": false,
"notNull": false
},
"name": {
"name": "name",
"type": "text",
"primaryKey": false,
"notNull": false
},
"primary_color_hex": {
"name": "primary_color_hex",
"type": "varchar(6)",
"primaryKey": false,
"notNull": true,
"default": "'c4ced4'"
},
"secondary_color_hex": {
"name": "secondary_color_hex",
"type": "varchar(6)",
"primaryKey": false,
"notNull": true,
"default": "'27251f'"
},
"text_color_hex": {
"name": "text_color_hex",
"type": "varchar(6)",
"primaryKey": false,
"notNull": true,
"default": "'ffffff'"
},
"title": {
"name": "title",
"type": "text",
"primaryKey": false,
"notNull": false,
"default": "'The best blog in the world!'"
},
"subtitle": {
"name": "subtitle",
"type": "text",
"primaryKey": false,
"notNull": false,
"default": "'Some extra info about the best blog in the world!'"
},
"domain": {
"name": "domain",
"type": "text",
"primaryKey": false,
"notNull": false,
"default": "NULL"
},
"send_freebie": {
"name": "send_freebie",
"type": "boolean",
"primaryKey": false,
"notNull": false,
"default": false
},
"freebie_name": {
"name": "freebie_name",
"type": "text",
"primaryKey": false,
"notNull": false,
"default": "''"
},
"freebie_url": {
"name": "freebie_url",
"type": "text",
"primaryKey": false,
"notNull": false,
"default": "''"
},
"freebie_text": {
"name": "freebie_text",
"type": "text",
"primaryKey": false,
"notNull": false,
"default": "''"
},
"freebie_image_url": {
"name": "freebie_image_url",
"type": "text",
"primaryKey": false,
"notNull": false,
"default": "''"
},
"subdomain_slug": {
"name": "subdomain_slug",
"type": "text",
"primaryKey": false,
"notNull": false
},
"social_medias": {
"name": "social_medias",
"type": "jsonb",
"primaryKey": false,
"notNull": false
},
"auto_publish": {
"name": "auto_publish",
"type": "boolean",
"primaryKey": false,
"notNull": false,
"default": false
},
"pubsub_expiry": {
"name": "pubsub_expiry",
"type": "timestamp",
"primaryKey": false,
"notNull": false
}
},
"indexes": {},
"foreignKeys": {
"sites_user_id_users_id_fk": {
"name": "sites_user_id_users_id_fk",
"tableFrom": "sites",
"tableTo": "users",
"columnsFrom": [
"user_id"
],
"columnsTo": [
"id"
],
"onDelete": "no action",
"onUpdate": "no action"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {
"sites_domain_unique": {
"name": "sites_domain_unique",
"nullsNotDistinct": false,
"columns": [
"domain"
]
}
}
},
"public.users": {
"name": "users",
"schema": "",
"columns": {
"id": {
"name": "id",
"type": "uuid",
"primaryKey": true,
"notNull": true,
"default": "gen_random_uuid()"
},
"google_id": {
"name": "google_id",
"type": "text",
"primaryKey": false,
"notNull": false
},
"stripe_id": {
"name": "stripe_id",
"type": "text",
"primaryKey": false,
"notNull": false
},
"name": {
"name": "name",
"type": "text",
"primaryKey": false,
"notNull": false
},
"email": {
"name": "email",
"type": "text",
"primaryKey": false,
"notNull": false
},
"channel_id": {
"name": "channel_id",
"type": "text",
"primaryKey": false,
"notNull": false
},
"uploads_playlist_id": {
"name": "uploads_playlist_id",
"type": "text",
"primaryKey": false,
"notNull": false
},
"subscription_tier": {
"name": "subscription_tier",
"type": "subscription_tier",
"typeSchema": "public",
"primaryKey": false,
"notNull": true,
"default": "'free'"
},
"tokens": {
"name": "tokens",
"type": "integer",
"primaryKey": false,
"notNull": true,
"default": 200
}
},
"indexes": {},
"foreignKeys": {},
"compositePrimaryKeys": {},
"uniqueConstraints": {}
},
"public.waitlist": {
"name": "waitlist",
"schema": "",
"columns": {
"email": {
"name": "email",
"type": "text",
"primaryKey": false,
"notNull": false
}
},
"indexes": {},
"foreignKeys": {},
"compositePrimaryKeys": {},
"uniqueConstraints": {}
}
},
"enums": {
"public.article_status": {
"name": "article_status",
"schema": "public",
"values": [
"queued",
"transcribing",
"generating",
"done"
]
},
"public.subscription_tier": {
"name": "subscription_tier",
"schema": "public",
"values": [
"free",
"basic",
"pro",
"enterprise"
]
}
},
"schemas": {},
"_meta": {
"columns": {},
"schemas": {},
"tables": {}
}
}

View File

@ -1,486 +0,0 @@
{
"id": "1b69d63a-4517-42bc-aa1f-0ae6ed4ac685",
"prevId": "9e95f80e-79e7-4af0-9a8e-327fb358ec26",
"version": "7",
"dialect": "postgresql",
"tables": {
"public.articles": {
"name": "articles",
"schema": "",
"columns": {
"id": {
"name": "id",
"type": "uuid",
"primaryKey": true,
"notNull": true,
"default": "gen_random_uuid()"
},
"site_id": {
"name": "site_id",
"type": "uuid",
"primaryKey": false,
"notNull": false
},
"content": {
"name": "content",
"type": "text",
"primaryKey": false,
"notNull": false
},
"source_video_id": {
"name": "source_video_id",
"type": "text",
"primaryKey": false,
"notNull": false
},
"title": {
"name": "title",
"type": "text",
"primaryKey": false,
"notNull": false
},
"seo_slug": {
"name": "seo_slug",
"type": "text",
"primaryKey": false,
"notNull": false
},
"is_public": {
"name": "is_public",
"type": "boolean",
"primaryKey": false,
"notNull": false,
"default": false
},
"views": {
"name": "views",
"type": "integer",
"primaryKey": false,
"notNull": false,
"default": 0
},
"seo_title": {
"name": "seo_title",
"type": "text",
"primaryKey": false,
"notNull": false
},
"seo_description": {
"name": "seo_description",
"type": "text",
"primaryKey": false,
"notNull": false
},
"excerp": {
"name": "excerp",
"type": "text",
"primaryKey": false,
"notNull": false
},
"status": {
"name": "status",
"type": "article_status",
"typeSchema": "public",
"primaryKey": false,
"notNull": false,
"default": "'queued'"
},
"created_at": {
"name": "created_at",
"type": "timestamp",
"primaryKey": false,
"notNull": false,
"default": "now()"
}
},
"indexes": {},
"foreignKeys": {
"articles_site_id_sites_id_fk": {
"name": "articles_site_id_sites_id_fk",
"tableFrom": "articles",
"tableTo": "sites",
"columnsFrom": [
"site_id"
],
"columnsTo": [
"id"
],
"onDelete": "no action",
"onUpdate": "no action"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {}
},
"public.sessions": {
"name": "sessions",
"schema": "",
"columns": {
"id": {
"name": "id",
"type": "uuid",
"primaryKey": true,
"notNull": true,
"default": "gen_random_uuid()"
},
"user_id": {
"name": "user_id",
"type": "uuid",
"primaryKey": false,
"notNull": false
},
"google_access_token": {
"name": "google_access_token",
"type": "text",
"primaryKey": false,
"notNull": false
},
"google_refresh_token": {
"name": "google_refresh_token",
"type": "text",
"primaryKey": false,
"notNull": false
},
"expires_at": {
"name": "expires_at",
"type": "timestamp",
"primaryKey": false,
"notNull": false
}
},
"indexes": {},
"foreignKeys": {
"sessions_user_id_users_id_fk": {
"name": "sessions_user_id_users_id_fk",
"tableFrom": "sessions",
"tableTo": "users",
"columnsFrom": [
"user_id"
],
"columnsTo": [
"id"
],
"onDelete": "no action",
"onUpdate": "no action"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {}
},
"public.signups": {
"name": "signups",
"schema": "",
"columns": {
"email": {
"name": "email",
"type": "text",
"primaryKey": false,
"notNull": true
},
"site_id": {
"name": "site_id",
"type": "uuid",
"primaryKey": false,
"notNull": true
},
"source": {
"name": "source",
"type": "text",
"primaryKey": false,
"notNull": false
},
"created_at": {
"name": "created_at",
"type": "timestamp",
"primaryKey": false,
"notNull": true,
"default": "now()"
}
},
"indexes": {},
"foreignKeys": {
"signups_site_id_sites_id_fk": {
"name": "signups_site_id_sites_id_fk",
"tableFrom": "signups",
"tableTo": "sites",
"columnsFrom": [
"site_id"
],
"columnsTo": [
"id"
],
"onDelete": "no action",
"onUpdate": "no action"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {}
},
"public.sites": {
"name": "sites",
"schema": "",
"columns": {
"id": {
"name": "id",
"type": "uuid",
"primaryKey": true,
"notNull": true,
"default": "gen_random_uuid()"
},
"user_id": {
"name": "user_id",
"type": "uuid",
"primaryKey": false,
"notNull": false
},
"name": {
"name": "name",
"type": "text",
"primaryKey": false,
"notNull": false
},
"primary_color_hex": {
"name": "primary_color_hex",
"type": "varchar(6)",
"primaryKey": false,
"notNull": true,
"default": "'c4ced4'"
},
"secondary_color_hex": {
"name": "secondary_color_hex",
"type": "varchar(6)",
"primaryKey": false,
"notNull": true,
"default": "'27251f'"
},
"text_color_hex": {
"name": "text_color_hex",
"type": "varchar(6)",
"primaryKey": false,
"notNull": true,
"default": "'ffffff'"
},
"title": {
"name": "title",
"type": "text",
"primaryKey": false,
"notNull": false,
"default": "'The best blog in the world!'"
},
"subtitle": {
"name": "subtitle",
"type": "text",
"primaryKey": false,
"notNull": false,
"default": "'Some extra info about the best blog in the world!'"
},
"domain": {
"name": "domain",
"type": "text",
"primaryKey": false,
"notNull": false,
"default": "NULL"
},
"send_freebie": {
"name": "send_freebie",
"type": "boolean",
"primaryKey": false,
"notNull": false,
"default": false
},
"freebie_name": {
"name": "freebie_name",
"type": "text",
"primaryKey": false,
"notNull": false,
"default": "''"
},
"freebie_url": {
"name": "freebie_url",
"type": "text",
"primaryKey": false,
"notNull": false,
"default": "''"
},
"freebie_text": {
"name": "freebie_text",
"type": "text",
"primaryKey": false,
"notNull": false,
"default": "''"
},
"freebie_image_url": {
"name": "freebie_image_url",
"type": "text",
"primaryKey": false,
"notNull": false,
"default": "''"
},
"subdomain_slug": {
"name": "subdomain_slug",
"type": "text",
"primaryKey": false,
"notNull": false
},
"social_medias": {
"name": "social_medias",
"type": "jsonb",
"primaryKey": false,
"notNull": false
},
"auto_publish": {
"name": "auto_publish",
"type": "boolean",
"primaryKey": false,
"notNull": false,
"default": false
},
"pubsub_expiry": {
"name": "pubsub_expiry",
"type": "timestamp",
"primaryKey": false,
"notNull": false
}
},
"indexes": {},
"foreignKeys": {
"sites_user_id_users_id_fk": {
"name": "sites_user_id_users_id_fk",
"tableFrom": "sites",
"tableTo": "users",
"columnsFrom": [
"user_id"
],
"columnsTo": [
"id"
],
"onDelete": "no action",
"onUpdate": "no action"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {
"sites_domain_unique": {
"name": "sites_domain_unique",
"nullsNotDistinct": false,
"columns": [
"domain"
]
}
}
},
"public.users": {
"name": "users",
"schema": "",
"columns": {
"id": {
"name": "id",
"type": "uuid",
"primaryKey": true,
"notNull": true,
"default": "gen_random_uuid()"
},
"google_id": {
"name": "google_id",
"type": "text",
"primaryKey": false,
"notNull": false
},
"stripe_id": {
"name": "stripe_id",
"type": "text",
"primaryKey": false,
"notNull": false
},
"name": {
"name": "name",
"type": "text",
"primaryKey": false,
"notNull": false
},
"email": {
"name": "email",
"type": "text",
"primaryKey": false,
"notNull": false
},
"channel_id": {
"name": "channel_id",
"type": "text",
"primaryKey": false,
"notNull": false
},
"uploads_playlist_id": {
"name": "uploads_playlist_id",
"type": "text",
"primaryKey": false,
"notNull": false
},
"subscription_tier": {
"name": "subscription_tier",
"type": "subscription_tier",
"typeSchema": "public",
"primaryKey": false,
"notNull": true,
"default": "'free'"
},
"tokens": {
"name": "tokens",
"type": "integer",
"primaryKey": false,
"notNull": true,
"default": 200
}
},
"indexes": {},
"foreignKeys": {},
"compositePrimaryKeys": {},
"uniqueConstraints": {}
},
"public.waitlist": {
"name": "waitlist",
"schema": "",
"columns": {
"email": {
"name": "email",
"type": "text",
"primaryKey": false,
"notNull": false
}
},
"indexes": {},
"foreignKeys": {},
"compositePrimaryKeys": {},
"uniqueConstraints": {}
}
},
"enums": {
"public.article_status": {
"name": "article_status",
"schema": "public",
"values": [
"queued",
"transcribing",
"generating",
"done",
"error"
]
},
"public.subscription_tier": {
"name": "subscription_tier",
"schema": "public",
"values": [
"free",
"basic",
"pro",
"enterprise"
]
}
},
"schemas": {},
"_meta": {
"columns": {},
"schemas": {},
"tables": {}
}
}

View File

@ -1,499 +0,0 @@
{
"id": "dcc6e5bd-8fea-4462-8f49-5cfc09408ab7",
"prevId": "1b69d63a-4517-42bc-aa1f-0ae6ed4ac685",
"version": "7",
"dialect": "postgresql",
"tables": {
"public.articles": {
"name": "articles",
"schema": "",
"columns": {
"id": {
"name": "id",
"type": "uuid",
"primaryKey": true,
"notNull": true,
"default": "gen_random_uuid()"
},
"site_id": {
"name": "site_id",
"type": "uuid",
"primaryKey": false,
"notNull": false
},
"content": {
"name": "content",
"type": "text",
"primaryKey": false,
"notNull": false
},
"source_video_id": {
"name": "source_video_id",
"type": "text",
"primaryKey": false,
"notNull": false
},
"title": {
"name": "title",
"type": "text",
"primaryKey": false,
"notNull": false
},
"seo_slug": {
"name": "seo_slug",
"type": "text",
"primaryKey": false,
"notNull": false
},
"is_public": {
"name": "is_public",
"type": "boolean",
"primaryKey": false,
"notNull": false,
"default": false
},
"views": {
"name": "views",
"type": "integer",
"primaryKey": false,
"notNull": false,
"default": 0
},
"seo_title": {
"name": "seo_title",
"type": "text",
"primaryKey": false,
"notNull": false
},
"seo_description": {
"name": "seo_description",
"type": "text",
"primaryKey": false,
"notNull": false
},
"excerp": {
"name": "excerp",
"type": "text",
"primaryKey": false,
"notNull": false
},
"status": {
"name": "status",
"type": "article_status",
"typeSchema": "public",
"primaryKey": false,
"notNull": false,
"default": "'queued'"
},
"created_at": {
"name": "created_at",
"type": "timestamp",
"primaryKey": false,
"notNull": false,
"default": "now()"
}
},
"indexes": {},
"foreignKeys": {
"articles_site_id_sites_id_fk": {
"name": "articles_site_id_sites_id_fk",
"tableFrom": "articles",
"tableTo": "sites",
"columnsFrom": [
"site_id"
],
"columnsTo": [
"id"
],
"onDelete": "no action",
"onUpdate": "no action"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {}
},
"public.sessions": {
"name": "sessions",
"schema": "",
"columns": {
"id": {
"name": "id",
"type": "uuid",
"primaryKey": true,
"notNull": true,
"default": "gen_random_uuid()"
},
"user_id": {
"name": "user_id",
"type": "uuid",
"primaryKey": false,
"notNull": false
},
"google_access_token": {
"name": "google_access_token",
"type": "text",
"primaryKey": false,
"notNull": false
},
"google_refresh_token": {
"name": "google_refresh_token",
"type": "text",
"primaryKey": false,
"notNull": false
},
"expires_at": {
"name": "expires_at",
"type": "timestamp",
"primaryKey": false,
"notNull": false
}
},
"indexes": {},
"foreignKeys": {
"sessions_user_id_users_id_fk": {
"name": "sessions_user_id_users_id_fk",
"tableFrom": "sessions",
"tableTo": "users",
"columnsFrom": [
"user_id"
],
"columnsTo": [
"id"
],
"onDelete": "no action",
"onUpdate": "no action"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {}
},
"public.signups": {
"name": "signups",
"schema": "",
"columns": {
"email": {
"name": "email",
"type": "text",
"primaryKey": false,
"notNull": true
},
"site_id": {
"name": "site_id",
"type": "uuid",
"primaryKey": false,
"notNull": true
},
"source": {
"name": "source",
"type": "text",
"primaryKey": false,
"notNull": false
},
"created_at": {
"name": "created_at",
"type": "timestamp",
"primaryKey": false,
"notNull": true,
"default": "now()"
}
},
"indexes": {},
"foreignKeys": {
"signups_site_id_sites_id_fk": {
"name": "signups_site_id_sites_id_fk",
"tableFrom": "signups",
"tableTo": "sites",
"columnsFrom": [
"site_id"
],
"columnsTo": [
"id"
],
"onDelete": "no action",
"onUpdate": "no action"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {}
},
"public.sites": {
"name": "sites",
"schema": "",
"columns": {
"id": {
"name": "id",
"type": "uuid",
"primaryKey": true,
"notNull": true,
"default": "gen_random_uuid()"
},
"user_id": {
"name": "user_id",
"type": "uuid",
"primaryKey": false,
"notNull": false
},
"name": {
"name": "name",
"type": "text",
"primaryKey": false,
"notNull": false
},
"primary_color_hex": {
"name": "primary_color_hex",
"type": "varchar(6)",
"primaryKey": false,
"notNull": true,
"default": "'c4ced4'"
},
"secondary_color_hex": {
"name": "secondary_color_hex",
"type": "varchar(6)",
"primaryKey": false,
"notNull": true,
"default": "'27251f'"
},
"text_color_hex": {
"name": "text_color_hex",
"type": "varchar(6)",
"primaryKey": false,
"notNull": true,
"default": "'ffffff'"
},
"title": {
"name": "title",
"type": "text",
"primaryKey": false,
"notNull": false,
"default": "'The best blog in the world!'"
},
"subtitle": {
"name": "subtitle",
"type": "text",
"primaryKey": false,
"notNull": false,
"default": "'Some extra info about the best blog in the world!'"
},
"domain": {
"name": "domain",
"type": "text",
"primaryKey": false,
"notNull": false,
"default": "NULL"
},
"send_freebie": {
"name": "send_freebie",
"type": "boolean",
"primaryKey": false,
"notNull": false,
"default": false
},
"freebie_name": {
"name": "freebie_name",
"type": "text",
"primaryKey": false,
"notNull": false,
"default": "'Our newsletter'"
},
"freebie_url": {
"name": "freebie_url",
"type": "text",
"primaryKey": false,
"notNull": false,
"default": "''"
},
"freebie_text": {
"name": "freebie_text",
"type": "text",
"primaryKey": false,
"notNull": false,
"default": "''"
},
"freebie_image_url": {
"name": "freebie_image_url",
"type": "text",
"primaryKey": false,
"notNull": false,
"default": "''"
},
"subdomain_slug": {
"name": "subdomain_slug",
"type": "text",
"primaryKey": false,
"notNull": false
},
"social_medias": {
"name": "social_medias",
"type": "jsonb",
"primaryKey": false,
"notNull": false
},
"auto_publish": {
"name": "auto_publish",
"type": "boolean",
"primaryKey": false,
"notNull": false,
"default": false
},
"pubsub_expiry": {
"name": "pubsub_expiry",
"type": "timestamp",
"primaryKey": false,
"notNull": false
},
"use_contact_page": {
"name": "use_contact_page",
"type": "boolean",
"primaryKey": false,
"notNull": false,
"default": false
},
"contact_email": {
"name": "contact_email",
"type": "text",
"primaryKey": false,
"notNull": false
}
},
"indexes": {},
"foreignKeys": {
"sites_user_id_users_id_fk": {
"name": "sites_user_id_users_id_fk",
"tableFrom": "sites",
"tableTo": "users",
"columnsFrom": [
"user_id"
],
"columnsTo": [
"id"
],
"onDelete": "no action",
"onUpdate": "no action"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {
"sites_domain_unique": {
"name": "sites_domain_unique",
"nullsNotDistinct": false,
"columns": [
"domain"
]
}
}
},
"public.users": {
"name": "users",
"schema": "",
"columns": {
"id": {
"name": "id",
"type": "uuid",
"primaryKey": true,
"notNull": true,
"default": "gen_random_uuid()"
},
"google_id": {
"name": "google_id",
"type": "text",
"primaryKey": false,
"notNull": false
},
"stripe_id": {
"name": "stripe_id",
"type": "text",
"primaryKey": false,
"notNull": false
},
"name": {
"name": "name",
"type": "text",
"primaryKey": false,
"notNull": false
},
"email": {
"name": "email",
"type": "text",
"primaryKey": false,
"notNull": false
},
"channel_id": {
"name": "channel_id",
"type": "text",
"primaryKey": false,
"notNull": false
},
"uploads_playlist_id": {
"name": "uploads_playlist_id",
"type": "text",
"primaryKey": false,
"notNull": false
},
"subscription_tier": {
"name": "subscription_tier",
"type": "subscription_tier",
"typeSchema": "public",
"primaryKey": false,
"notNull": true,
"default": "'free'"
},
"tokens": {
"name": "tokens",
"type": "integer",
"primaryKey": false,
"notNull": true,
"default": 200
}
},
"indexes": {},
"foreignKeys": {},
"compositePrimaryKeys": {},
"uniqueConstraints": {}
},
"public.waitlist": {
"name": "waitlist",
"schema": "",
"columns": {
"email": {
"name": "email",
"type": "text",
"primaryKey": false,
"notNull": false
}
},
"indexes": {},
"foreignKeys": {},
"compositePrimaryKeys": {},
"uniqueConstraints": {}
}
},
"enums": {
"public.article_status": {
"name": "article_status",
"schema": "public",
"values": [
"queued",
"transcribing",
"generating",
"done",
"error"
]
},
"public.subscription_tier": {
"name": "subscription_tier",
"schema": "public",
"values": [
"free",
"basic",
"pro",
"enterprise"
]
}
},
"schemas": {},
"_meta": {
"columns": {},
"schemas": {},
"tables": {}
}
}

View File

@ -1,512 +0,0 @@
{
"id": "de65dcf5-f6de-434b-80b6-ba199df3a96a",
"prevId": "dcc6e5bd-8fea-4462-8f49-5cfc09408ab7",
"version": "7",
"dialect": "postgresql",
"tables": {
"public.articles": {
"name": "articles",
"schema": "",
"columns": {
"id": {
"name": "id",
"type": "uuid",
"primaryKey": true,
"notNull": true,
"default": "gen_random_uuid()"
},
"site_id": {
"name": "site_id",
"type": "uuid",
"primaryKey": false,
"notNull": false
},
"content": {
"name": "content",
"type": "text",
"primaryKey": false,
"notNull": false
},
"source_video_id": {
"name": "source_video_id",
"type": "text",
"primaryKey": false,
"notNull": false
},
"title": {
"name": "title",
"type": "text",
"primaryKey": false,
"notNull": false
},
"seo_slug": {
"name": "seo_slug",
"type": "text",
"primaryKey": false,
"notNull": false
},
"is_public": {
"name": "is_public",
"type": "boolean",
"primaryKey": false,
"notNull": false,
"default": false
},
"views": {
"name": "views",
"type": "integer",
"primaryKey": false,
"notNull": false,
"default": 0
},
"seo_title": {
"name": "seo_title",
"type": "text",
"primaryKey": false,
"notNull": false
},
"seo_description": {
"name": "seo_description",
"type": "text",
"primaryKey": false,
"notNull": false
},
"excerp": {
"name": "excerp",
"type": "text",
"primaryKey": false,
"notNull": false
},
"status": {
"name": "status",
"type": "article_status",
"typeSchema": "public",
"primaryKey": false,
"notNull": false,
"default": "'queued'"
},
"created_at": {
"name": "created_at",
"type": "timestamp",
"primaryKey": false,
"notNull": false,
"default": "now()"
}
},
"indexes": {},
"foreignKeys": {
"articles_site_id_sites_id_fk": {
"name": "articles_site_id_sites_id_fk",
"tableFrom": "articles",
"tableTo": "sites",
"columnsFrom": [
"site_id"
],
"columnsTo": [
"id"
],
"onDelete": "no action",
"onUpdate": "no action"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {}
},
"public.sessions": {
"name": "sessions",
"schema": "",
"columns": {
"id": {
"name": "id",
"type": "uuid",
"primaryKey": true,
"notNull": true,
"default": "gen_random_uuid()"
},
"user_id": {
"name": "user_id",
"type": "uuid",
"primaryKey": false,
"notNull": false
},
"google_access_token": {
"name": "google_access_token",
"type": "text",
"primaryKey": false,
"notNull": false
},
"google_refresh_token": {
"name": "google_refresh_token",
"type": "text",
"primaryKey": false,
"notNull": false
},
"expires_at": {
"name": "expires_at",
"type": "timestamp",
"primaryKey": false,
"notNull": false
}
},
"indexes": {},
"foreignKeys": {
"sessions_user_id_users_id_fk": {
"name": "sessions_user_id_users_id_fk",
"tableFrom": "sessions",
"tableTo": "users",
"columnsFrom": [
"user_id"
],
"columnsTo": [
"id"
],
"onDelete": "no action",
"onUpdate": "no action"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {}
},
"public.signups": {
"name": "signups",
"schema": "",
"columns": {
"email": {
"name": "email",
"type": "text",
"primaryKey": false,
"notNull": true
},
"site_id": {
"name": "site_id",
"type": "uuid",
"primaryKey": false,
"notNull": true
},
"source": {
"name": "source",
"type": "text",
"primaryKey": false,
"notNull": false
},
"created_at": {
"name": "created_at",
"type": "timestamp",
"primaryKey": false,
"notNull": true,
"default": "now()"
}
},
"indexes": {},
"foreignKeys": {
"signups_site_id_sites_id_fk": {
"name": "signups_site_id_sites_id_fk",
"tableFrom": "signups",
"tableTo": "sites",
"columnsFrom": [
"site_id"
],
"columnsTo": [
"id"
],
"onDelete": "no action",
"onUpdate": "no action"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {}
},
"public.sites": {
"name": "sites",
"schema": "",
"columns": {
"id": {
"name": "id",
"type": "uuid",
"primaryKey": true,
"notNull": true,
"default": "gen_random_uuid()"
},
"user_id": {
"name": "user_id",
"type": "uuid",
"primaryKey": false,
"notNull": false
},
"name": {
"name": "name",
"type": "text",
"primaryKey": false,
"notNull": false
},
"primary_color_hex": {
"name": "primary_color_hex",
"type": "varchar(6)",
"primaryKey": false,
"notNull": true,
"default": "'c4ced4'"
},
"secondary_color_hex": {
"name": "secondary_color_hex",
"type": "varchar(6)",
"primaryKey": false,
"notNull": true,
"default": "'27251f'"
},
"text_color_hex": {
"name": "text_color_hex",
"type": "varchar(6)",
"primaryKey": false,
"notNull": true,
"default": "'ffffff'"
},
"title": {
"name": "title",
"type": "text",
"primaryKey": false,
"notNull": false,
"default": "'The best blog in the world!'"
},
"subtitle": {
"name": "subtitle",
"type": "text",
"primaryKey": false,
"notNull": false,
"default": "'Some extra info about the best blog in the world!'"
},
"domain": {
"name": "domain",
"type": "text",
"primaryKey": false,
"notNull": false,
"default": "NULL"
},
"send_freebie": {
"name": "send_freebie",
"type": "boolean",
"primaryKey": false,
"notNull": false,
"default": false
},
"freebie_name": {
"name": "freebie_name",
"type": "text",
"primaryKey": false,
"notNull": false,
"default": "'Our newsletter'"
},
"freebie_url": {
"name": "freebie_url",
"type": "text",
"primaryKey": false,
"notNull": false,
"default": "''"
},
"freebie_text": {
"name": "freebie_text",
"type": "text",
"primaryKey": false,
"notNull": false,
"default": "''"
},
"freebie_image_url": {
"name": "freebie_image_url",
"type": "text",
"primaryKey": false,
"notNull": false,
"default": "''"
},
"subdomain_slug": {
"name": "subdomain_slug",
"type": "text",
"primaryKey": false,
"notNull": false
},
"social_medias": {
"name": "social_medias",
"type": "jsonb",
"primaryKey": false,
"notNull": false
},
"auto_publish": {
"name": "auto_publish",
"type": "boolean",
"primaryKey": false,
"notNull": false,
"default": false
},
"pubsub_expiry": {
"name": "pubsub_expiry",
"type": "timestamp",
"primaryKey": false,
"notNull": false
},
"use_contact_page": {
"name": "use_contact_page",
"type": "boolean",
"primaryKey": false,
"notNull": false,
"default": false
},
"contact_email": {
"name": "contact_email",
"type": "text",
"primaryKey": false,
"notNull": false
},
"use_about_page": {
"name": "use_about_page",
"type": "boolean",
"primaryKey": false,
"notNull": false,
"default": true
},
"about_text": {
"name": "about_text",
"type": "text",
"primaryKey": false,
"notNull": false
}
},
"indexes": {},
"foreignKeys": {
"sites_user_id_users_id_fk": {
"name": "sites_user_id_users_id_fk",
"tableFrom": "sites",
"tableTo": "users",
"columnsFrom": [
"user_id"
],
"columnsTo": [
"id"
],
"onDelete": "no action",
"onUpdate": "no action"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {
"sites_domain_unique": {
"name": "sites_domain_unique",
"nullsNotDistinct": false,
"columns": [
"domain"
]
}
}
},
"public.users": {
"name": "users",
"schema": "",
"columns": {
"id": {
"name": "id",
"type": "uuid",
"primaryKey": true,
"notNull": true,
"default": "gen_random_uuid()"
},
"google_id": {
"name": "google_id",
"type": "text",
"primaryKey": false,
"notNull": false
},
"stripe_id": {
"name": "stripe_id",
"type": "text",
"primaryKey": false,
"notNull": false
},
"name": {
"name": "name",
"type": "text",
"primaryKey": false,
"notNull": false
},
"email": {
"name": "email",
"type": "text",
"primaryKey": false,
"notNull": false
},
"channel_id": {
"name": "channel_id",
"type": "text",
"primaryKey": false,
"notNull": false
},
"uploads_playlist_id": {
"name": "uploads_playlist_id",
"type": "text",
"primaryKey": false,
"notNull": false
},
"subscription_tier": {
"name": "subscription_tier",
"type": "subscription_tier",
"typeSchema": "public",
"primaryKey": false,
"notNull": true,
"default": "'free'"
},
"tokens": {
"name": "tokens",
"type": "integer",
"primaryKey": false,
"notNull": true,
"default": 200
}
},
"indexes": {},
"foreignKeys": {},
"compositePrimaryKeys": {},
"uniqueConstraints": {}
},
"public.waitlist": {
"name": "waitlist",
"schema": "",
"columns": {
"email": {
"name": "email",
"type": "text",
"primaryKey": false,
"notNull": false
}
},
"indexes": {},
"foreignKeys": {},
"compositePrimaryKeys": {},
"uniqueConstraints": {}
}
},
"enums": {
"public.article_status": {
"name": "article_status",
"schema": "public",
"values": [
"queued",
"transcribing",
"generating",
"done",
"error"
]
},
"public.subscription_tier": {
"name": "subscription_tier",
"schema": "public",
"values": [
"free",
"basic",
"pro",
"enterprise"
]
}
},
"schemas": {},
"_meta": {
"columns": {},
"schemas": {},
"tables": {}
}
}

View File

@ -1,525 +0,0 @@
{
"id": "674d04c7-a853-4f20-98ac-b92e33d6d017",
"prevId": "de65dcf5-f6de-434b-80b6-ba199df3a96a",
"version": "7",
"dialect": "postgresql",
"tables": {
"public.articles": {
"name": "articles",
"schema": "",
"columns": {
"id": {
"name": "id",
"type": "uuid",
"primaryKey": true,
"notNull": true,
"default": "gen_random_uuid()"
},
"site_id": {
"name": "site_id",
"type": "uuid",
"primaryKey": false,
"notNull": false
},
"content": {
"name": "content",
"type": "text",
"primaryKey": false,
"notNull": false
},
"source_video_id": {
"name": "source_video_id",
"type": "text",
"primaryKey": false,
"notNull": false
},
"title": {
"name": "title",
"type": "text",
"primaryKey": false,
"notNull": false
},
"seo_slug": {
"name": "seo_slug",
"type": "text",
"primaryKey": false,
"notNull": false
},
"is_public": {
"name": "is_public",
"type": "boolean",
"primaryKey": false,
"notNull": false,
"default": false
},
"views": {
"name": "views",
"type": "integer",
"primaryKey": false,
"notNull": false,
"default": 0
},
"seo_title": {
"name": "seo_title",
"type": "text",
"primaryKey": false,
"notNull": false
},
"seo_description": {
"name": "seo_description",
"type": "text",
"primaryKey": false,
"notNull": false
},
"excerp": {
"name": "excerp",
"type": "text",
"primaryKey": false,
"notNull": false
},
"status": {
"name": "status",
"type": "article_status",
"typeSchema": "public",
"primaryKey": false,
"notNull": false,
"default": "'queued'"
},
"created_at": {
"name": "created_at",
"type": "timestamp",
"primaryKey": false,
"notNull": false,
"default": "now()"
}
},
"indexes": {},
"foreignKeys": {
"articles_site_id_sites_id_fk": {
"name": "articles_site_id_sites_id_fk",
"tableFrom": "articles",
"tableTo": "sites",
"columnsFrom": [
"site_id"
],
"columnsTo": [
"id"
],
"onDelete": "no action",
"onUpdate": "no action"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {}
},
"public.sessions": {
"name": "sessions",
"schema": "",
"columns": {
"id": {
"name": "id",
"type": "uuid",
"primaryKey": true,
"notNull": true,
"default": "gen_random_uuid()"
},
"user_id": {
"name": "user_id",
"type": "uuid",
"primaryKey": false,
"notNull": false
},
"google_access_token": {
"name": "google_access_token",
"type": "text",
"primaryKey": false,
"notNull": false
},
"google_refresh_token": {
"name": "google_refresh_token",
"type": "text",
"primaryKey": false,
"notNull": false
},
"expires_at": {
"name": "expires_at",
"type": "timestamp",
"primaryKey": false,
"notNull": false
}
},
"indexes": {},
"foreignKeys": {
"sessions_user_id_users_id_fk": {
"name": "sessions_user_id_users_id_fk",
"tableFrom": "sessions",
"tableTo": "users",
"columnsFrom": [
"user_id"
],
"columnsTo": [
"id"
],
"onDelete": "no action",
"onUpdate": "no action"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {}
},
"public.signups": {
"name": "signups",
"schema": "",
"columns": {
"email": {
"name": "email",
"type": "text",
"primaryKey": false,
"notNull": true
},
"site_id": {
"name": "site_id",
"type": "uuid",
"primaryKey": false,
"notNull": true
},
"source": {
"name": "source",
"type": "text",
"primaryKey": false,
"notNull": false
},
"created_at": {
"name": "created_at",
"type": "timestamp",
"primaryKey": false,
"notNull": true,
"default": "now()"
}
},
"indexes": {},
"foreignKeys": {
"signups_site_id_sites_id_fk": {
"name": "signups_site_id_sites_id_fk",
"tableFrom": "signups",
"tableTo": "sites",
"columnsFrom": [
"site_id"
],
"columnsTo": [
"id"
],
"onDelete": "no action",
"onUpdate": "no action"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {}
},
"public.sites": {
"name": "sites",
"schema": "",
"columns": {
"id": {
"name": "id",
"type": "uuid",
"primaryKey": true,
"notNull": true,
"default": "gen_random_uuid()"
},
"user_id": {
"name": "user_id",
"type": "uuid",
"primaryKey": false,
"notNull": false
},
"name": {
"name": "name",
"type": "text",
"primaryKey": false,
"notNull": false
},
"primary_color_hex": {
"name": "primary_color_hex",
"type": "varchar(6)",
"primaryKey": false,
"notNull": true,
"default": "'c4ced4'"
},
"secondary_color_hex": {
"name": "secondary_color_hex",
"type": "varchar(6)",
"primaryKey": false,
"notNull": true,
"default": "'27251f'"
},
"text_color_hex": {
"name": "text_color_hex",
"type": "varchar(6)",
"primaryKey": false,
"notNull": true,
"default": "'ffffff'"
},
"title": {
"name": "title",
"type": "text",
"primaryKey": false,
"notNull": false,
"default": "'The best blog in the world!'"
},
"subtitle": {
"name": "subtitle",
"type": "text",
"primaryKey": false,
"notNull": false,
"default": "'Some extra info about the best blog in the world!'"
},
"domain": {
"name": "domain",
"type": "text",
"primaryKey": false,
"notNull": false,
"default": "NULL"
},
"send_freebie": {
"name": "send_freebie",
"type": "boolean",
"primaryKey": false,
"notNull": false,
"default": false
},
"freebie_name": {
"name": "freebie_name",
"type": "text",
"primaryKey": false,
"notNull": false,
"default": "'Our newsletter'"
},
"freebie_url": {
"name": "freebie_url",
"type": "text",
"primaryKey": false,
"notNull": false,
"default": "''"
},
"freebie_text": {
"name": "freebie_text",
"type": "text",
"primaryKey": false,
"notNull": false,
"default": "''"
},
"freebie_image_url": {
"name": "freebie_image_url",
"type": "text",
"primaryKey": false,
"notNull": false,
"default": "''"
},
"subdomain_slug": {
"name": "subdomain_slug",
"type": "text",
"primaryKey": false,
"notNull": false
},
"social_medias": {
"name": "social_medias",
"type": "jsonb",
"primaryKey": false,
"notNull": false
},
"auto_publish": {
"name": "auto_publish",
"type": "boolean",
"primaryKey": false,
"notNull": false,
"default": false
},
"pubsub_expiry": {
"name": "pubsub_expiry",
"type": "timestamp",
"primaryKey": false,
"notNull": false
},
"use_contact_page": {
"name": "use_contact_page",
"type": "boolean",
"primaryKey": false,
"notNull": false,
"default": false
},
"contact_email": {
"name": "contact_email",
"type": "text",
"primaryKey": false,
"notNull": false
},
"use_about_page": {
"name": "use_about_page",
"type": "boolean",
"primaryKey": false,
"notNull": false,
"default": true
},
"about_text": {
"name": "about_text",
"type": "text",
"primaryKey": false,
"notNull": false
}
},
"indexes": {},
"foreignKeys": {
"sites_user_id_users_id_fk": {
"name": "sites_user_id_users_id_fk",
"tableFrom": "sites",
"tableTo": "users",
"columnsFrom": [
"user_id"
],
"columnsTo": [
"id"
],
"onDelete": "no action",
"onUpdate": "no action"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {
"sites_domain_unique": {
"name": "sites_domain_unique",
"nullsNotDistinct": false,
"columns": [
"domain"
]
}
}
},
"public.users": {
"name": "users",
"schema": "",
"columns": {
"id": {
"name": "id",
"type": "uuid",
"primaryKey": true,
"notNull": true,
"default": "gen_random_uuid()"
},
"google_id": {
"name": "google_id",
"type": "text",
"primaryKey": false,
"notNull": false
},
"stripe_id": {
"name": "stripe_id",
"type": "text",
"primaryKey": false,
"notNull": false
},
"name": {
"name": "name",
"type": "text",
"primaryKey": false,
"notNull": false
},
"email": {
"name": "email",
"type": "text",
"primaryKey": false,
"notNull": false
},
"channel_id": {
"name": "channel_id",
"type": "text",
"primaryKey": false,
"notNull": false
},
"uploads_playlist_id": {
"name": "uploads_playlist_id",
"type": "text",
"primaryKey": false,
"notNull": false
},
"subscription_tier": {
"name": "subscription_tier",
"type": "subscription_tier",
"typeSchema": "public",
"primaryKey": false,
"notNull": true,
"default": "'free'"
},
"subscribed_until": {
"name": "subscribed_until",
"type": "date",
"primaryKey": false,
"notNull": false,
"default": "'1970-01-01'"
},
"tokens_claimed": {
"name": "tokens_claimed",
"type": "date",
"primaryKey": false,
"notNull": false
},
"tokens": {
"name": "tokens",
"type": "integer",
"primaryKey": false,
"notNull": true,
"default": 200
}
},
"indexes": {},
"foreignKeys": {},
"compositePrimaryKeys": {},
"uniqueConstraints": {}
},
"public.waitlist": {
"name": "waitlist",
"schema": "",
"columns": {
"email": {
"name": "email",
"type": "text",
"primaryKey": false,
"notNull": false
}
},
"indexes": {},
"foreignKeys": {},
"compositePrimaryKeys": {},
"uniqueConstraints": {}
}
},
"enums": {
"public.article_status": {
"name": "article_status",
"schema": "public",
"values": [
"queued",
"transcribing",
"generating",
"done",
"error"
]
},
"public.subscription_tier": {
"name": "subscription_tier",
"schema": "public",
"values": [
"free",
"basic",
"pro",
"enterprise"
]
}
},
"schemas": {},
"_meta": {
"columns": {},
"schemas": {},
"tables": {}
}
}

View File

@ -1,532 +0,0 @@
{
"id": "5df89ef6-dcab-4661-87f4-ad81b88bf4f4",
"prevId": "674d04c7-a853-4f20-98ac-b92e33d6d017",
"version": "7",
"dialect": "postgresql",
"tables": {
"public.articles": {
"name": "articles",
"schema": "",
"columns": {
"id": {
"name": "id",
"type": "uuid",
"primaryKey": true,
"notNull": true,
"default": "gen_random_uuid()"
},
"site_id": {
"name": "site_id",
"type": "uuid",
"primaryKey": false,
"notNull": false
},
"content": {
"name": "content",
"type": "text",
"primaryKey": false,
"notNull": false
},
"source_video_id": {
"name": "source_video_id",
"type": "text",
"primaryKey": false,
"notNull": false
},
"title": {
"name": "title",
"type": "text",
"primaryKey": false,
"notNull": false
},
"seo_slug": {
"name": "seo_slug",
"type": "text",
"primaryKey": false,
"notNull": false
},
"is_public": {
"name": "is_public",
"type": "boolean",
"primaryKey": false,
"notNull": false,
"default": false
},
"views": {
"name": "views",
"type": "integer",
"primaryKey": false,
"notNull": false,
"default": 0
},
"seo_title": {
"name": "seo_title",
"type": "text",
"primaryKey": false,
"notNull": false
},
"seo_description": {
"name": "seo_description",
"type": "text",
"primaryKey": false,
"notNull": false
},
"excerp": {
"name": "excerp",
"type": "text",
"primaryKey": false,
"notNull": false
},
"status": {
"name": "status",
"type": "article_status",
"typeSchema": "public",
"primaryKey": false,
"notNull": false,
"default": "'queued'"
},
"created_at": {
"name": "created_at",
"type": "timestamp",
"primaryKey": false,
"notNull": false,
"default": "now()"
}
},
"indexes": {},
"foreignKeys": {
"articles_site_id_sites_id_fk": {
"name": "articles_site_id_sites_id_fk",
"tableFrom": "articles",
"tableTo": "sites",
"columnsFrom": [
"site_id"
],
"columnsTo": [
"id"
],
"onDelete": "no action",
"onUpdate": "no action"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {}
},
"public.sessions": {
"name": "sessions",
"schema": "",
"columns": {
"id": {
"name": "id",
"type": "uuid",
"primaryKey": true,
"notNull": true,
"default": "gen_random_uuid()"
},
"user_id": {
"name": "user_id",
"type": "uuid",
"primaryKey": false,
"notNull": false
},
"google_access_token": {
"name": "google_access_token",
"type": "text",
"primaryKey": false,
"notNull": false
},
"google_refresh_token": {
"name": "google_refresh_token",
"type": "text",
"primaryKey": false,
"notNull": false
},
"expires_at": {
"name": "expires_at",
"type": "timestamp",
"primaryKey": false,
"notNull": false
}
},
"indexes": {},
"foreignKeys": {
"sessions_user_id_users_id_fk": {
"name": "sessions_user_id_users_id_fk",
"tableFrom": "sessions",
"tableTo": "users",
"columnsFrom": [
"user_id"
],
"columnsTo": [
"id"
],
"onDelete": "no action",
"onUpdate": "no action"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {}
},
"public.signups": {
"name": "signups",
"schema": "",
"columns": {
"email": {
"name": "email",
"type": "text",
"primaryKey": false,
"notNull": true
},
"site_id": {
"name": "site_id",
"type": "uuid",
"primaryKey": false,
"notNull": true
},
"source": {
"name": "source",
"type": "text",
"primaryKey": false,
"notNull": false
},
"created_at": {
"name": "created_at",
"type": "timestamp",
"primaryKey": false,
"notNull": true,
"default": "now()"
}
},
"indexes": {},
"foreignKeys": {
"signups_site_id_sites_id_fk": {
"name": "signups_site_id_sites_id_fk",
"tableFrom": "signups",
"tableTo": "sites",
"columnsFrom": [
"site_id"
],
"columnsTo": [
"id"
],
"onDelete": "no action",
"onUpdate": "no action"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {}
},
"public.sites": {
"name": "sites",
"schema": "",
"columns": {
"id": {
"name": "id",
"type": "uuid",
"primaryKey": true,
"notNull": true,
"default": "gen_random_uuid()"
},
"user_id": {
"name": "user_id",
"type": "uuid",
"primaryKey": false,
"notNull": false
},
"name": {
"name": "name",
"type": "text",
"primaryKey": false,
"notNull": false
},
"primary_color_hex": {
"name": "primary_color_hex",
"type": "varchar(6)",
"primaryKey": false,
"notNull": true,
"default": "'c4ced4'"
},
"secondary_color_hex": {
"name": "secondary_color_hex",
"type": "varchar(6)",
"primaryKey": false,
"notNull": true,
"default": "'27251f'"
},
"text_color_hex": {
"name": "text_color_hex",
"type": "varchar(6)",
"primaryKey": false,
"notNull": true,
"default": "'ffffff'"
},
"title": {
"name": "title",
"type": "text",
"primaryKey": false,
"notNull": false,
"default": "'The best blog in the world!'"
},
"subtitle": {
"name": "subtitle",
"type": "text",
"primaryKey": false,
"notNull": false,
"default": "'Some extra info about the best blog in the world!'"
},
"domain": {
"name": "domain",
"type": "text",
"primaryKey": false,
"notNull": false,
"default": "NULL"
},
"send_freebie": {
"name": "send_freebie",
"type": "boolean",
"primaryKey": false,
"notNull": false,
"default": false
},
"freebie_name": {
"name": "freebie_name",
"type": "text",
"primaryKey": false,
"notNull": false,
"default": "'Our newsletter'"
},
"freebie_url": {
"name": "freebie_url",
"type": "text",
"primaryKey": false,
"notNull": false,
"default": "''"
},
"freebie_text": {
"name": "freebie_text",
"type": "text",
"primaryKey": false,
"notNull": false,
"default": "''"
},
"freebie_image_url": {
"name": "freebie_image_url",
"type": "text",
"primaryKey": false,
"notNull": false,
"default": "''"
},
"subdomain_slug": {
"name": "subdomain_slug",
"type": "text",
"primaryKey": false,
"notNull": false
},
"social_medias": {
"name": "social_medias",
"type": "jsonb",
"primaryKey": false,
"notNull": false
},
"auto_publish": {
"name": "auto_publish",
"type": "boolean",
"primaryKey": false,
"notNull": false,
"default": false
},
"pubsub_expiry": {
"name": "pubsub_expiry",
"type": "timestamp",
"primaryKey": false,
"notNull": false
},
"use_contact_page": {
"name": "use_contact_page",
"type": "boolean",
"primaryKey": false,
"notNull": false,
"default": false
},
"contact_email": {
"name": "contact_email",
"type": "text",
"primaryKey": false,
"notNull": false
},
"use_about_page": {
"name": "use_about_page",
"type": "boolean",
"primaryKey": false,
"notNull": false,
"default": true
},
"about_text": {
"name": "about_text",
"type": "text",
"primaryKey": false,
"notNull": false
}
},
"indexes": {},
"foreignKeys": {
"sites_user_id_users_id_fk": {
"name": "sites_user_id_users_id_fk",
"tableFrom": "sites",
"tableTo": "users",
"columnsFrom": [
"user_id"
],
"columnsTo": [
"id"
],
"onDelete": "no action",
"onUpdate": "no action"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {
"sites_domain_unique": {
"name": "sites_domain_unique",
"nullsNotDistinct": false,
"columns": [
"domain"
]
}
}
},
"public.users": {
"name": "users",
"schema": "",
"columns": {
"id": {
"name": "id",
"type": "uuid",
"primaryKey": true,
"notNull": true,
"default": "gen_random_uuid()"
},
"google_id": {
"name": "google_id",
"type": "text",
"primaryKey": false,
"notNull": false
},
"stripe_id": {
"name": "stripe_id",
"type": "text",
"primaryKey": false,
"notNull": false
},
"name": {
"name": "name",
"type": "text",
"primaryKey": false,
"notNull": false
},
"email": {
"name": "email",
"type": "text",
"primaryKey": false,
"notNull": false
},
"channel_id": {
"name": "channel_id",
"type": "text",
"primaryKey": false,
"notNull": false
},
"uploads_playlist_id": {
"name": "uploads_playlist_id",
"type": "text",
"primaryKey": false,
"notNull": false
},
"subscription_tier": {
"name": "subscription_tier",
"type": "subscription_tier",
"typeSchema": "public",
"primaryKey": false,
"notNull": true,
"default": "'free'"
},
"subscribed_until": {
"name": "subscribed_until",
"type": "date",
"primaryKey": false,
"notNull": false,
"default": "'1970-01-01'"
},
"tokens_claimed": {
"name": "tokens_claimed",
"type": "date",
"primaryKey": false,
"notNull": false
},
"trial_used": {
"name": "trial_used",
"type": "boolean",
"primaryKey": false,
"notNull": false,
"default": false
},
"tokens": {
"name": "tokens",
"type": "integer",
"primaryKey": false,
"notNull": true,
"default": 200
}
},
"indexes": {},
"foreignKeys": {},
"compositePrimaryKeys": {},
"uniqueConstraints": {}
},
"public.waitlist": {
"name": "waitlist",
"schema": "",
"columns": {
"email": {
"name": "email",
"type": "text",
"primaryKey": false,
"notNull": false
}
},
"indexes": {},
"foreignKeys": {},
"compositePrimaryKeys": {},
"uniqueConstraints": {}
}
},
"enums": {
"public.article_status": {
"name": "article_status",
"schema": "public",
"values": [
"queued",
"transcribing",
"generating",
"done",
"error"
]
},
"public.subscription_tier": {
"name": "subscription_tier",
"schema": "public",
"values": [
"free",
"basic",
"pro",
"enterprise"
]
}
},
"schemas": {},
"_meta": {
"columns": {},
"schemas": {},
"tables": {}
}
}

View File

@ -4,115 +4,10 @@
"entries": [ "entries": [
{ {
"idx": 0, "idx": 0,
"version": "7", "version": "5",
"when": 1717763053901, "when": 1686356934306,
"tag": "0000_gorgeous_amazoness", "tag": "0000_plain_killmonger",
"breakpoints": true "breakpoints": false
},
{
"idx": 1,
"version": "7",
"when": 1717763195953,
"tag": "0001_big_sugar_man",
"breakpoints": true
},
{
"idx": 2,
"version": "7",
"when": 1717793012414,
"tag": "0002_perpetual_norman_osborn",
"breakpoints": true
},
{
"idx": 3,
"version": "7",
"when": 1717849103779,
"tag": "0003_organic_dark_beast",
"breakpoints": true
},
{
"idx": 4,
"version": "7",
"when": 1717852643512,
"tag": "0004_lively_frank_castle",
"breakpoints": true
},
{
"idx": 5,
"version": "7",
"when": 1717852812991,
"tag": "0005_smiling_pandemic",
"breakpoints": true
},
{
"idx": 6,
"version": "7",
"when": 1718267048369,
"tag": "0006_happy_red_wolf",
"breakpoints": true
},
{
"idx": 7,
"version": "7",
"when": 1718353625117,
"tag": "0007_early_living_mummy",
"breakpoints": true
},
{
"idx": 8,
"version": "7",
"when": 1718354167998,
"tag": "0008_smart_captain_cross",
"breakpoints": true
},
{
"idx": 9,
"version": "7",
"when": 1718818623333,
"tag": "0009_wooden_weapon_omega",
"breakpoints": true
},
{
"idx": 10,
"version": "7",
"when": 1718818704691,
"tag": "0010_complex_malice",
"breakpoints": true
},
{
"idx": 11,
"version": "7",
"when": 1719007763731,
"tag": "0011_normal_thunderball",
"breakpoints": true
},
{
"idx": 12,
"version": "7",
"when": 1719133327271,
"tag": "0012_magenta_captain_marvel",
"breakpoints": true
},
{
"idx": 13,
"version": "7",
"when": 1719134353536,
"tag": "0013_lucky_vulture",
"breakpoints": true
},
{
"idx": 14,
"version": "7",
"when": 1719389425401,
"tag": "0014_tan_bill_hollister",
"breakpoints": true
},
{
"idx": 15,
"version": "7",
"when": 1719403126188,
"tag": "0015_nervous_victor_mancha",
"breakpoints": true
} }
] ]
} }

View File

@ -1,94 +1,7 @@
import { sql } from "drizzle-orm"; import { pgTable, serial, text, varchar } from "drizzle-orm/pg-core";
import { boolean, date, integer, jsonb, pgEnum, pgTable, text, timestamp, unique, uniqueIndex, uuid, varchar } from "drizzle-orm/pg-core";
export const subscription_enum = pgEnum("subscription_tier", ["free", "basic", "pro", "enterprise"])
export const article_status_enum = pgEnum("article_status", ["queued", "transcribing", "generating", "done", "error"])
export const users = pgTable("users", { export const users = pgTable("users", {
id: uuid("id").defaultRandom().primaryKey(), id: serial("id").primaryKey(),
google_id: text("google_id"), fullName: text("full_name"),
stripe_id: text("stripe_id"), phone: varchar("phone", { length: 256 }),
name: text("name"),
email: text("email"),
channel_id: text("channel_id"),
uploads_playlist_id: text("uploads_playlist_id"),
subscription_tier: subscription_enum("subscription_tier").default("free").notNull(),
subscribed_until: date("subscribed_until").default(`1970-01-01`),
tokens_claimed: date("tokens_claimed"),
trial_used: boolean("trial_used").default(false),
tokens: integer("tokens").notNull()
});
export const sessions = pgTable("sessions", {
id: uuid("id").defaultRandom().primaryKey(),
user_id: uuid("user_id").references(() => users.id),
access_token: text("google_access_token"),
refresh_token: text("google_refresh_token"),
expires_at: timestamp("expires_at")
});
export function makeid(length) {
let result = '';
const characters = 'abcdefghijklmnopqrstuvwxyz';
const charactersLength = characters.length;
let counter = 0;
while (counter < length) {
result += characters.charAt(Math.floor(Math.random() * charactersLength));
counter += 1;
}
return result;
}
export const sites = pgTable("sites", {
id: uuid("id").defaultRandom().primaryKey(),
user_id: uuid("user_id").references(() => users.id),
name: text("name"),
primary_color_hex: varchar("primary_color_hex", { length: 6 }).default("c4ced4").notNull(),
secondary_color_hex: varchar("secondary_color_hex", { length: 6 }).default('27251f').notNull(),
text_color_hex: varchar("text_color_hex", { length: 6 }).default('ffffff').notNull(),
title: text("title").default("The best blog in the world!"),
subtitle: text("subtitle").default("Some extra info about the best blog in the world!"),
domain: text("domain").default(sql`NULL`).unique(),
use_freebie: boolean("send_freebie").default(false),
freebie_title: text("freebie_name").default("Our newsletter"),
freebie_url: text("freebie_url").default(""),
freebie_text: text("freebie_text").default(""),
freebie_image_url: text("freebie_image_url").default(""),
subdomain_slug: text("subdomain_slug").$defaultFn(() => {
return makeid(10);
}),
social_medias: jsonb("social_medias"),
auto_publish: boolean("auto_publish").default(false),
pubsub_expiry: timestamp("pubsub_expiry"),
use_contact_page: boolean("use_contact_page").default(false),
contact_email: text("contact_email"),
use_about_page: boolean("use_about_page").default(true),
about_text: text("about_text")
});
export const articles = pgTable("articles", {
id: uuid("id").defaultRandom().primaryKey(),
site_id: uuid("site_id").references(() => sites.id),
content: text("content"),
source_video_id: text("source_video_id"),
title: text("title"),
seo_slug: text("seo_slug"),
is_public: boolean("is_public").default(false),
views: integer("views").default(0),
meta_title: text("seo_title"),
meta_desc: text("seo_description"),
excerp: text("excerp"),
status: article_status_enum("status").default("queued"),
created_at: timestamp("created_at").defaultNow()
})
export const signups = pgTable("signups", {
email: text("email").notNull(),
site_id: uuid("site_id").references(() => sites.id).notNull(),
source: text("source"),
created_at: timestamp("created_at").defaultNow().notNull()
});
export const waitlist = pgTable("waitlist", {
email: text("email")
}); });

15
src/fastify.d.ts vendored
View File

@ -1,21 +1,10 @@
import {OAuth2Namespace} from "@fastify/oauth2";
import type { db } from "./db"; import type { db } from "./db";
import type { Redis } from "./utils"; import type { Redis } from "./utils";
declare module "fastify" { declare module "fastify" {
// eslint-disable-next-line @typescript-eslint/consistent-type-definitions // eslint-disable-next-line @typescript-eslint/consistent-type-definitions
interface FastifyRequest { interface FastifyRequest {
// db: typeof db; db: typeof db;
// redis: typeof Redis; redis: typeof Redis;
session?: {
id: string,
user_id: string,
access_token: string,
refresh_token: string,
expires_at: Date
}
}
interface FastifyInstance {
googleOAuth2: OAuth2Namespace
} }
} }

View File

@ -1,49 +1,19 @@
import { initDb } from "./db/index.js"; import { initDb } from "./db/index.js";
import { channelRoutes, authRoutes, videoRoutes, meRoutes, blogRoutes, dashboardRoutes, webhookRoutes } from "./routes/index.js"; import { testRoutes } from "./routes/index.js";
import { env, Logger } from "./utils/index.js"; import { env, Logger, Redis } from "./utils/index.js";
import fastify from "fastify"; import fastify from "fastify";
import { middleware } from "./modules/middleware.js"; import { middleware } from "./modules/middleware.js";
import oauth from '@fastify/oauth2';
import fastifyCookie from "@fastify/cookie";
import xml2js from 'xml2js';
import './utils/sentry.js';
// import fastifyMultipart from "@fastify/multipart";
import * as Sentry from "@sentry/node";
import rawBody from 'fastify-raw-body';
const API_VERSION = "v1";
const API_VERSION = "v1";
export const main = async () => { export const main = async () => {
const server = fastify({ const server = fastify({
bodyLimit: 1_000_000, bodyLimit: 1_000_000,
trustProxy: true, trustProxy: true,
// logger: true
}); });
Sentry.setupFastifyErrorHandler(server);
await initDb(); await initDb();
// await Redis.initialize(); await Redis.initialize();
// server.register(fastifyMultipart, {
// // attachFieldsToBody: true,
// });
server.register(fastifyCookie, {
secret: "my-secret", // for cookies signature
hook: 'preParsing', // set to false to disable cookie autoparsing or set autoparsing on any of the following hooks: 'onRequest', 'preParsing', 'preHandler', 'preValidation'. default: 'onRequest'
parseOptions: {}, // options for parsing cookies
});
server.register(rawBody, {
field: 'rawBody', // change the default request.rawBody property name
global: false, // add the rawBody to every request. **Default true**
encoding: 'utf8', // set it to false to set rawBody as a Buffer **Default utf8**
runFirst: true, // get the body before any preParsing hook change/uncompress it. **Default false**
routes: [], // array of routes, **`global`** will be ignored, wildcard routes not supported
jsonContentTypes: [], // array of content-types to handle as JSON. **Default ['application/json']**
})
server.register(middleware); server.register(middleware);
server.register(import("@fastify/cors"), { server.register(import("@fastify/cors"), {
@ -52,66 +22,11 @@ export const main = async () => {
credentials: true, credentials: true,
}); });
server.register(oauth, {
name: 'googleOAuth2',
scope: ["https://www.googleapis.com/auth/userinfo.email", "https://www.googleapis.com/auth/userinfo.profile"],
credentials: {
client: {
id: env.GOOGLE_CLIENT_ID,
secret: env.GOOGLE_SECRET
},
auth: oauth.GOOGLE_CONFIGURATION
},
callbackUriParams: {
// custom query param that will be passed to callbackUri
access_type: 'offline', // will tell Google to send a refreshToken too
},
pkce: 'S256',
// register a fastify url to start the redirect flow
startRedirectPath: '/auth/google',
// google redirect here after the user login
callbackUri: `${env.PUBLIC_API_URL}/auth/google/callback`
});
server.addContentTypeParser(['text/xml', 'application/xml', 'application/atom+xml'], { parseAs: 'string' }, function (request, payload, done) {
xml2js.parseString(payload, function (err, body) {
done(err, body)
})
})
// Routes // Routes
server.register(channelRoutes, { server.register(testRoutes, {
prefix: `/channels`, prefix: `/${API_VERSION}/test`,
}); });
server.register(videoRoutes, {
prefix: `/videos`,
});
server.register(authRoutes, {
prefix: `/auth`,
});
server.register(meRoutes, {
prefix: `/me`
});
server.register(blogRoutes, {
prefix: `/blog`
});
server.register(dashboardRoutes, {
prefix: `/dashboard`
});
server.register(webhookRoutes, {
prefix: `/webhooks`
});
server.get("/hello", (req, res) => {
res.send({ message: "world", cookies: req.cookies });
})
server.listen({ host: env.HOST, port: env.PORT }, (error, address) => { server.listen({ host: env.HOST, port: env.PORT }, (error, address) => {
if (error) { if (error) {
Logger.error("INIT", error.message); Logger.error("INIT", error.message);

View File

@ -2,48 +2,17 @@
import fp from "fastify-plugin"; import fp from "fastify-plugin";
import { Redis } from "../utils/index.js"; import { Redis } from "../utils/index.js";
import { db } from "../db/index.js"; import { db } from "../db/index.js";
import { sessions } from "../db/schemas.js";
import { eq } from "drizzle-orm";
export const middleware = fp( const middleware = fp(
/** /**
* @param {FastifyInstance} fastify * @param {FastifyInstance} fastify
* @param {unknown} _options * @param {unknown} _options
*/ */
async (fastify, _options) => { async (fastify, _options) => {
fastify.addHook("onRequest", async (request) => { fastify.addHook("onRequest", async (request) => {
// request.redis = Redis; request.redis = Redis;
// request.db = db; request.db = db;
}); });
}); });
export const authMiddlewareFn = async (request, response) => { export { middleware };
try {
// if (!request.headers.authorization || !request.headers.authorization.startsWith("Bearer")) {
if (!request.cookies.token) {
response.status(401).send({ success: false, message: "Missing authentication token" });
return false;
}
// const token = request.headers.authorization.split(" ")[1];
const token = request.cookies.token;
const session = await db.select().from(sessions).where(eq(sessions.id, token));
if (session.length == 0) {
response.status(401).send({ success: false, message: "Invalid authentication token" });
return false;
}
// console.log(token);
// console.log(session);
request.session = session[0];
return true;
} catch (e) {
console.log(e);
}
}
export const authMiddleware = fp(
async (fastify, _options) => {
fastify.addHook("preValidation", authMiddlewareFn);
}
)

View File

@ -1,145 +0,0 @@
import { createSession as createSession } from '../utils/token.js';
import { getUserInfo } from '../utils/youtube.js';
import { db } from '../db/index.js';
import { sessions, sites, users, users as usersTable } from '../db/schemas.js';
import { eq } from 'drizzle-orm';
import { env } from '../utils/env.js';
import { authMiddleware, authMiddlewareFn } from '../modules/middleware.js';
import { createCustomer } from '../utils/stripe.js';
/** @typedef {import("fastify").FastifyInstance} FastifyInstance */
/**
*
* @param {FastifyInstance} fastify
* @param {unknown} _
* @param {() => void} done
*/
export const authRoutes = (fastify, _, done) => {
fastify.get("/google/callback", async (request, reply) => {
let token = "";
try {
/** @type {{token: import('@fastify/oauth2').Token}} */
const token_response = await fastify.googleOAuth2.getAccessTokenFromAuthorizationCodeFlow(request);
token = token_response.token;
const user_info = await getUserInfo(token.access_token);
if (!user_info.verified_email) {
reply.status(400).send({ success: false, message: "Provider email is not verified" });
return;
}
//const channel_info = await getChannelInfo(token.access_token);
const [existing_user] = await db.select().from(usersTable).where(eq(usersTable.google_id, user_info.id));
let user;
let session_id;
await db.transaction(async tx => {
try {
if (existing_user) {
user = existing_user;
if (token.refresh_token) {
await tx.update(sessions).set({
refresh_token: token.refresh_token
}).where(eq(sessions.user_id, user.id));
}
}
else {
const customer = await createCustomer(user_info.email, user_info.name);
[user] = await tx.insert(usersTable).values({
name: user_info.name,
google_id: user_info.id,
email: user_info.email,
// channel_id: channel_info.id,
channel_id: "TEMP_DISABLED",
// uploads_playlist_id: channel_info.contentDetails.relatedPlaylists.uploads,
uploads_playlist_id: "TEMP_DISABLED",
stripe_id: customer.id
}).returning({ id: usersTable.id });
// console.log("USER MADE")
// console.log("user", user);
await tx.insert(sites).values({
name: `${user_info.name}'s Website`,
user_id: user.id
});
console.log("SITE MADE")
if (user.length == 0) {
reply.status(400).send({ success: false, message: "Problem when creating user account" });
return;
}
}
let existing_session = await tx.select().from(sessions).where(eq(sessions.user_id, user.id));
if (!existing_session || existing_session.length === 0) {
let session_info = await createSession(user.id, {
access_token: token.access_token,
refresh_token: token.refresh_token,
expires_at: new Date(token.expires_at)
}, tx);
console.log("SESSION MADE")
session_id = session_info.session_id;
}
else {
let session_info = await tx.select().from(users).leftJoin(sessions, eq(sessions.user_id, users.id)).where(eq(users.google_id, user_info.id));
if (!session_info || session_info.length == 0) {
reply.status(400).send({ success: false, message: "Problem when creating user account" });
await tx.rollback();
return;
}
console.log("session_info", session_info);
session_id = session_info[0].sessions.id
}
} catch (e) {
console.log(e);
await tx.rollback();
return;
}
})
reply.setCookie("token", session_id, {
httpOnly: true,
path: "/",
sameSite: false,
maxAge: 1000 * 60 * 60 * 24 * 7,
domain: "api.youpage.ai"
});
reply.redirect(env.FRONTEND_URL + "/auth?token=" + session_id);
// response.send({
// token: session_id
// });
return;
} catch (e) {
if(e.message === "no_channel") {
reply.status(400).send("Your account does not have a youtube channel. Please make one.")
}
// fastify.googleOAuth2.revokeToken(token, "refresh_token");
console.log(e);
reply.send({ success: false, message: "There was a problem when making your account." });
return;
}
});
fastify.post("/logout", {
preValidation: authMiddlewareFn
}, async (req, reply) => {
try {
await db.delete(sessions).where(eq(sessions.id, req.session.id));
// await fastify.googleOAuth2.revokeToken(req.session, "refresh_token", {
// "content-type": "application/json"
// });
console.log("revoked")
// console.log(await res.json());
reply.send({
success: true
});
} catch (e) {
console.log(e);
}
})
done();
};

View File

@ -1,214 +0,0 @@
import { and, desc, eq, not, or, sql } from "drizzle-orm";
import { db } from "../db/index.js";
import { authMiddlewareFn } from "../modules/middleware.js";
import { articles, articles as articlesTable, signups as signupsTable, sites, users } from "../db/schemas.js";
import { sendContactEmail, sendFreebie } from "../utils/email.js";
/**
*
* @param {import("fastify").FastifyInstance} fastify
* @param {unknown} _
* @param {() => void} done
*/
export const blogRoutes = (fastify, _, done) => {
fastify.get("/", {
schema: {
querystring: {
mine: {
type: "boolean",
},
blog_id: {
type: "string"
},
offset: {
type: "number"
}
}
}
}, async (request, response) => {
try {
const mine = request.query.mine ? request.query.mine : false;
const blog_id = request.query.blog_id;
if (!mine && blog_id === "") {
response.status(400).send({
success: false,
message: "Request can either have \"mine\" set to true or provide a blog_id"
});
return;
}
let clause;
let site;
if (mine) {
if (!(await authMiddlewareFn(request, response))) return;
try {
[site] = await db.select().from(sites).where(and(eq(sites.user_id, request.session.user_id)));
} catch (e) {
console.log(e);
}
}
else {
let referer = new URL(request.headers.origin).host;
try {
[site] = await db.select().from(sites).where(or(eq(sites.domain, referer), eq(sites.subdomain_slug, referer.split(".")[0])));
} catch (e) {
console.log(e);
}
}
if (!site) {
response.status(400).send({
success: false,
message: "Site not found"
});
return;
}
clause = eq(articlesTable.site_id, site.id);
if (!mine) {
clause = and(eq(articlesTable.site_id, site.id), eq(articlesTable.is_public, true));
}
// const access_token = await getAccessToken(fastify, request);
// const channel = await getChannelInfo(access_token);
const results = await db.select({
id: articlesTable.id,
title: articlesTable.title,
seo_slug: articlesTable.seo_slug,
views: articlesTable.views,
is_public: articlesTable.is_public,
created_at: articlesTable.created_at,
}).from(articlesTable).where(and(eq(articlesTable.status, "done"), clause)).limit(10).offset(request.query.offset || 0).orderBy(desc(articlesTable.created_at));
const [{ total }] = await db.select({
total: sql`COUNT(*)`
}).from(articlesTable).where(clause);
response.send({
success: true,
articles: results,
total_articles: total,
site
});
} catch (e) {
console.log(e);
response.status(500).send({
success: false,
message: "Unexpected error"
});
}
});
fastify.get("/article", {
schema: {
querystring: {
id: {
type: "string"
},
slug: {
type: "string"
}
}
}
}, async (req, reply) => {
try {
const [result] = await db.select().from(articlesTable)
.where((req.query.id ? eq(articlesTable.id, req.query.id) : eq(articlesTable.seo_slug, req.query.slug)));
if (!req.session && result) {
await db.update(articlesTable).set({
views: sql`${articles.views} + 1`
}).where(eq(articlesTable.seo_slug, req.query.slug));
}
reply.send({
success: true,
article: result
});
return;
} catch (e) {
console.log(e);
}
});
fastify.post("/signup", {
schema: {
body: {
site_id: {
type: "string"
},
email: {
type: "string",
format: "email"
},
source: {
type: "string"
}
}
}
}, async (req, reply) => {
const inserted = await db.insert(signupsTable).values({
email: req.body.email,
site_id: req.body.site_id,
source: req.body.source
});
if (inserted.rowCount != 1) {
reply.status(400).send({
success: false,
message: "Problem when signing up email"
});
return;
}
await sendFreebie(req.body.email, req.body.site_id)
reply.send({
success: true
});
});
fastify.post("/contact", {
body: {
email: {
type: "string",
format: "email"
},
subject: {
type: "string"
},
content: {
type: "string"
},
blog_id: {
type: "string"
}
}
}, async (req, reply) => {
const [blog] = await db.select().from(sites).where(eq(sites.id, req.body.blog_id));
if(!blog || !blog.contact_email) {
return reply.code(400).send({
success: false,
code: "invalid_blog"
});
}
try {
await sendContactEmail(blog.contact_email, req.body.email, req.body.subject, req.body.content);
} catch (e) {
console.log(e);
return reply.code(500).send({
success: false,
code: "problem_sending"
});
}
return reply.send({
success: true
});
});
done();
};

View File

@ -1,31 +0,0 @@
/** @typedef {import("fastify").FastifyInstance} FastifyInstance */
import { authMiddleware } from "../modules/middleware.js";
import { getAccessToken, getChannelInfo } from "../utils/youtube.js";
/**
*
* @param {FastifyInstance} fastify
* @param {unknown} _
* @param {() => void} done
*/
export const channelRoutes = (fastify, _, done) => {
fastify.register(authMiddleware);
fastify.get("/", async (request, response) => {
try {
const access_token = await getAccessToken(fastify, request);
const channel = await getChannelInfo(access_token);
response.send({
success: true,
channel
})
} catch (e) {
console.log(e);
}
});
done();
};

View File

@ -1,466 +0,0 @@
/** @typedef {import("fastify").FastifyInstance} FastifyInstance */
import { and, desc, eq, getTableColumns, inArray, notInArray, sql, not } from "drizzle-orm";
import { db } from "../db/index.js";
import { articles, articles as articlesTable, signups as signupsTable, sites, users } from "../db/schemas.js";
import { authMiddleware, authMiddlewareFn } from "../modules/middleware.js";
import { jsonToCsv, createBlogFromCaptions, createArticleSlug, getVideoById, env, getWhisperCaptions, getVideoDetails, getVideoWithCaptions } from "../utils/index.js";
import { escape } from "querystring";
const websubVerifyToken = "FQNI4Suzih";
/**
*
* @param {FastifyInstance} fastify
* @param {unknown} _
* @param {() => void} done
*/
export const dashboardRoutes = (fastify, _, done) => {
fastify.register(authMiddleware);
fastify.get("/", async (req, response) => {
try {
const [{ site_id, user_id }] = await db.select({
site_id: sites.id,
user_id: sites.user_id
}).from(sites).where(eq(sites.user_id, req.session.user_id));
const recentArticles = await db.select({
title: articlesTable.title,
views: articlesTable.views,
created_at: articlesTable.created_at
}).from(articlesTable)
.where(eq(articlesTable.site_id, site_id))
.orderBy(desc(articlesTable.created_at)).limit(5);
const recentSignups = await db.select({
email: signupsTable.email,
created_at: signupsTable.created_at
}).from(signupsTable)
.where(eq(signupsTable.site_id, site_id))
.orderBy(desc(signupsTable.created_at)).limit(8);
const [{ totalArticles }] = await db.select({ totalArticles: sql`count(*)` }).from(articlesTable).where(and(eq(articlesTable.site_id, site_id), eq(articlesTable.status, "done")));
const [{ totalViews }] = await db.select({ totalViews: sql`COALESCE(sum(${articlesTable.views}), 0)` }).from(articlesTable).where(eq(articlesTable.site_id, site_id));
const [{ totalEmails }] = await db.select({ totalEmails: sql`count(*)` }).from(signupsTable).where(eq(signupsTable.site_id, site_id));
response.send({
success: true,
recentArticles,
recentSignups,
totalArticles,
totalEmails,
totalViews
});
return;
} catch (e) {
console.log(e);
response.status(500).send({
success: false,
message: "Failed to get dashboard data",
log: e.message
});
return;
}
});
fastify.get("/email-export", async (req, response) => {
});
fastify.get("/signups", async (req, reply) => {
const site = await db.select().from(sites).where(eq(sites.user_id, req.session.user_id));
if (site[0]?.user_id != req.session.user_id) {
reply.status(403).send({ success: false });
return;
}
const signups = await db.select().from(signupsTable).where(eq(signupsTable.site_id, site[0].id)).orderBy(desc(signupsTable.created_at));
reply.send({
success: true,
signups
});
});
fastify.get("/signups/export", async (req, reply) => {
try {
const [{ site_id }] = await db.select({
site_id: sites.id
}).from(sites).where(eq(sites.user_id, req.session.user_id));
const signups = await db.select({
email: signupsTable.email,
source: sql`COALESCE(source, 'Home')`,
created_at: signupsTable.created_at
}).from(signupsTable).where(eq(signupsTable.site_id, site_id)).orderBy(desc(signupsTable.created_at));
let result = jsonToCsv(signups);
reply.send({
success: true,
data: result
});
return;
} catch (e) {
console.log(e);
}
});
fastify.put("/article", {
schema: {
body: {
type: "object",
properties: {
id: {
type: "string"
},
is_public: {
type: "boolean"
},
content: {
type: "string"
},
title: {
type: "string"
},
},
required: ["id"]
}
}
}, async (req, reply) => {
const [article] = await db.select(getTableColumns(articles)).from(articles).leftJoin(sites, eq(sites.id, articles.site_id)).where(eq(sites.user_id, req.session.user_id));
if (!article) {
reply.status(404).send({
success: false,
message: "This article does not exist."
});
return;
}
if (Object.keys(req.body).length > 1) {
await db.update(articles).set(JSON.parse(JSON.stringify({ ...req.body, id: undefined }))).where(eq(articles.id, req.body.id));
}
reply.send({
success: true
});
return;
});
fastify.delete("/article", {
schema: {
querystring: {
type: "object",
properties: {
id: {
type: "string"
}
},
required: ["id"]
}
}
}, async (req, reply) => {
const [article] = await db.select(getTableColumns(articles)).from(articles).leftJoin(sites, eq(sites.id, articles.site_id)).where(and(eq(sites.user_id, req.session.user_id), eq(articles.id, req.query.id)));
if (!article) {
reply.status(404).send({
success: false,
message: "This article does not exist."
});
return;
}
await db.delete(articles).where(eq(articles.id, article.id));
reply.send({
success: true
});
return;
});
fastify.post("/create", {
schema: {
body: {
type: "object",
required: ["video_id"],
properties: {
video_id: {
type: "string",
},
length: {
type: "number",
},
format: {
type: "string"
},
faq: {
type: "boolean"
},
}
},
}
}, async (req, reply) => {
let article;
try {
const [{ tokens }] = await db.select({
tokens: users.tokens
}).from(users).where(eq(users.id, req.session.user_id));
if (tokens < 1) {
reply.send({
success: false,
code: "insufficient_tokens",
message: "Insufficient tokens"
});
return;
}
// const access_token = await getAccessToken(fastify, req);
let urlRegex = /^.*(youtu.be\/|v\/|u\/\w\/|embed\/|watch\?v=|\&v=|\?v=)([^#\&\?]*).*/;
let match = req.body.video_id.match(urlRegex);
if (!match) {
reply.status(400).send({
success: false,
message: "Invalid Youtube URL"
});
return;
}
const site = await db.select().from(sites).where(eq(sites.user_id, req.session.user_id));
// const video_data = await getVideoDetails(req.body.video_id);
const video_data = await getVideoWithCaptions(req.body.video_id);
reply.send({
success: true
});
article = (await db.insert(articlesTable).values({
title: video_data.title,
source_video_id: match[2],
status: "transcribing",
site_id: site[0].id,
is_public: false
}).returning({ id: articlesTable.id }))[0];
// video_data.captions = await getWhisperCaptions(req.body.video_id);
// const video_data = await getVideoById(access_token, req.body.video_id);
await db.update(articlesTable).set({
status: "generating"
}).where(eq(articlesTable.id, article.id));
const blog_content_json = await createBlogFromCaptions(video_data.captions, { title: video_data.title, description: video_data.description }, req.body);
// TODO: once I add multiple sites per user, this should come from the client
await db.update(articlesTable).set({
title: blog_content_json.title,
content: blog_content_json.content,
meta_title: blog_content_json.meta_title,
meta_desc: blog_content_json.meta_desc,
excerp: blog_content_json.excerp,
seo_slug: createArticleSlug(blog_content_json.title),
is_public: false,
status: "done"
}).where(eq(articlesTable.id, article.id));
await db.update(users).set({
tokens: tokens - 1
}).where(eq(users.id, req.session.user_id));
} catch (e) {
console.log(e);
if(e.message == "no_captions") {
reply.status(400).send({
success: false,
message: "This video does not have captions!"
});
}
article ? await db.update(articlesTable).set({
status: "error"
}).where(eq(articlesTable.id, article.id)) : "";
let message = "problem_creating_article";
reply.status(500).send({
success: false,
message: message
})
}
});
fastify.get("/queue", async (req, reply) => {
try {
const site_id = (await db.select({ site_id: sites.id }).from(sites).where(eq(sites.user_id, req.session.user_id)))[0].site_id;
if (!site_id) throw new Error("Could not find site_id");
const queue = await db.select({
id: articlesTable.id,
title: articlesTable.title,
status: articlesTable.status,
created_at: articlesTable.created_at,
source_id: articlesTable.source_video_id
}).from(articlesTable)
.where(and(eq(articlesTable.site_id, site_id), notInArray(articles.status, ["done", "error"])))
.orderBy(desc(articlesTable.created_at));
reply.send({
success: true,
queue
});
} catch (err) {
reply.status(500).send({
success: false,
message: err.message
})
}
});
fastify.post("/recreate", {
schema: {
body: {
type: "object",
required: ["video_id"],
properties: {
video_id: {
type: "string",
},
length: {
type: "number",
},
format: {
type: "string"
},
faq: {
type: "boolean"
},
}
},
}
}, async (req, reply) => {
});
fastify.put("/website", {
schema: {
body: {
type: "object",
properties: {
id: {
type: "string"
},
name: {
type: "string"
},
primary_color_hex: {
type: "string",
maxLength: 6,
minLength: 6
},
secondary_color_hex: {
type: "string",
maxLength: 6,
minLength: 6
},
text_color_hex: {
type: "string",
maxLength: 6,
minLength: 6
},
use_freebie: {
type: "boolean"
},
freebie_title: {
type: ["string", "null"]
},
freebie_url: {
type: ["string", "null"],
format: "uri"
},
freebie_image_url: {
type: ["string", "null"],
format: "uri"
},
freebie_text: {
type: ["string", "null"]
},
title: {
type: "string",
},
subtitle: {
type: "string",
},
domain: {
type: ["string", "null"]
},
auto_publish: {
type: "boolean"
},
use_contact_page: {
type: "boolean"
},
contact_email: {
type: ["string", "null"],
format: "email"
}
},
required: ["id"]
}
},
preValidation: authMiddlewareFn
}, async (req, reply) => {
try {
const [site] = await db.select().from(sites).where(eq(sites.id, req.body.id));
if (site.user_id !== req.session.user_id) {
reply.status(403).send({
success: false
});
return;
}
if (env.CERTS_URL && site.domain !== req.body.domain) {
await fetch(`http://${env.CERTS_URL}/provision`, {
method: "POST",
body: JSON.stringify({
domain: req.body.domain
})
});
}
// if (site.auto_publish !== req.body.auto_publish) {
// const [user] = await db.select().from(users).where(eq(users.id, req.session.user_id));
// if (!user) throw new Error("Problem getting user");
// let form = new FormData();
// form.set("hub.callback", env.PUBLIC_API_URL + "/webhooks/youtube");
// form.set("hub.topic", "https://www.youtube.com/xml/feeds/videos.xml?channel_id=" + user.channel_id);
// form.set("hub.verify", "async");
// form.set("hub.mode", req.body.auto_publish ? "subscribe" : "unsubscribe");
// form.set("hub.verify_token", websubVerifyToken);
// await fetch("https://pubsubhubbub.appspot.com/subscribe", {
// method: "POST",
// body: form
// });
// }
const data = structuredClone(req.body);
delete data.id;
await db.update(sites).set(data).where(eq(sites.id, req.body.id));
return {
success: true
}
} catch (e) {
console.log(e);
}
});
done();
};

View File

@ -1,7 +1 @@
export * from "./auth.js"; export * from "./test.js";
export * from "./blog.js"
export * from "./channels.js";
export * from "./dashboard.js"
export * from "./me.js";
export * from "./videos.js";
export * from "./webhook.js";

View File

@ -1,87 +0,0 @@
/** @typedef {import("fastify").FastifyInstance} FastifyInstance */
import { eq } from "drizzle-orm";
import { db } from "../db/index.js";
import { users } from "../db/schemas.js";
import { authMiddleware } from "../modules/middleware.js";
import { createCheckout, getBillingDashboard } from "../utils/stripe.js";
import { env } from "../utils/env.js";
/**
*
* @param {FastifyInstance} fastify
* @param {unknown} _
* @param {() => void} done
*/
export const meRoutes = (fastify, _, done) => {
fastify.register(authMiddleware);
fastify.get("/", async (request, response) => {
try {
const user = await db.select().from(users).where(eq(users.id, request.session.user_id));
if (typeof user == typeof Error) {
response.status(400).send({
success: false,
message: "User not found"
});
return;
}
response.send({
success: true,
user: user[0],
is_subscribed: new Date(user[0].subscribed_until) > new Date(),
});
} catch (e) {
response.status(400).send({
success: false,
message: "User not found",
log: e.message
});
return;
}
});
fastify.get("/billing", async (request, response) => {
try {
const [user] = await db.select().from(users).where(eq(users.id, request.session.user_id));
const billing_session_url = await getBillingDashboard(user.stripe_id, env.FRONTEND_URL);
response.redirect(billing_session_url);
return;
} catch (e) {
response.status(400).send({
success: false,
message: "User not found",
log: e.message
});
return;
}
});
fastify.get("/subscribe", async (req, reply) => {
try {
const [user] = await db.select().from(users).where(eq(users.id, req.session.user_id));
if (!user) throw new Error("user not found");
if(new Date(user.subscribed_until) > new Date()) return reply.redirect(303, "/me/billing");
console.log(new Date(user.subscribed_until).getTime())
const checkout_url = await createCheckout(env.STRIPE_SUBSCRIPTION_PRICE_ID, user.stripe_id, {
user_id: user.id
}, {
...(!user.trial_used ? {trial_end: new Date(Date.now() + 15 * 24 * 60 * 60 * 1000)} : {})
});
reply.redirect(303, checkout_url);
} catch (e) {
reply.status(400).send({
success: false,
message: "User not found",
log: e.message
});
return;
}
});
done();
};

21
src/routes/test.js Normal file
View File

@ -0,0 +1,21 @@
/** @typedef {import("fastify").FastifyInstance} FastifyInstance */
/**
*
* @param {FastifyInstance} fastify
* @param {unknown} _
* @param {() => void} done
*/
export const testRoutes = (fastify, _, done) => {
fastify.get("/", async (request, response) => {
const visits = Number(await request.redis.get("visits"));
request.redis.incr("visits");
response.send({
hello: "world",
visits,
});
});
done();
};

View File

@ -1,33 +0,0 @@
/** @typedef {import("fastify").FastifyInstance} FastifyInstance */
import { eq } from "drizzle-orm";
import { db } from "../db/index.js";
import { users } from "../db/schemas.js";
import { authMiddleware } from "../modules/middleware.js";
import { getAccessToken, getVideosFromPlaylist } from "../utils/youtube.js";
/**
*
* @param {FastifyInstance} fastify
* @param {unknown} _
* @param {() => void} done
*/
export const videoRoutes = (fastify, _, done) => {
fastify.register(authMiddleware);
fastify.get("/", async (request, response) => {
try {
const token = await getAccessToken(fastify, request);
const [user] = await db.select().from(users).where(eq(users.id, request.session.user_id));
const videos = await getVideosFromPlaylist(token, user.uploads_playlist_id);
response.send({
success: true,
videos
});
} catch (e) {
console.log(e);
}
});
done();
};

View File

@ -1,110 +0,0 @@
/** @typedef {import("fastify").FastifyInstance} FastifyInstance */
import { eq } from "drizzle-orm";
import { db } from "../db/index.js";
import { articles, sites, users } from "../db/schemas.js";
import { getVideoWithCaptions } from "../utils/youtube.js";
import { createBlogFromCaptions } from "../utils/ai.js";
import { createArticleSlug } from "../utils/index.js";
import { handleWebhook } from "../utils/stripe.js";
/**
*
* @param {FastifyInstance} fastify
* @param {unknown} _
* @param {() => void} done
*/
export const webhookRoutes = (fastify, _, done) => {
fastify.post("/stripe", {
config: {
rawBody: true
},
handler: async (req, reply) => {
try {
await handleWebhook(req.rawBody, req.headers["stripe-signature"]);
reply.status(200).send({});
} catch (e) {
console.log(e);
reply.status(400).send({
success: false,
message: "Error validating purchase",
log: e.message
});
return;
}
}
});
fastify.get("/youtube", async (req, reply) => {
// Check if the request contains the 'hub.challenge' query parameter
if (req.query["hub.challenge"] && req.query["hub.verify_token"] === "FQNI4Suzih" && req.query["hub.topic"].startsWith("https://www.youtube.com/xml/feeds/videos.xml?channel_id=")) {
if (req.query["hub.mode" === "unsubscribe"]) return reply.code(200).send();
const channel = req.query["hub.topic"].replace("https://www.youtube.com/xml/feeds/videos.xml?channel_id=", "");
const [user] = await db.select().from(users).where(eq(users.channel_id, channel));
if (!user) return reply.code(400).send("Invalid user");
await db.update(sites).set({
pubsub_expiry: new Date(new Date().getTime() + (parseInt(req.query["hub.lease_seconds"] || 172800) * 1000))
}).where(eq(sites.user_id, user.id));
// Respond with the challenge to verify the subscription
console.log(req.query)
console.log("verifying...", req.query["hub.challenge"]);
return reply.send(req.query["hub.challenge"]);
} else {
// Handle other cases or errors
return reply.code(404).send("Not found");
}
});
fastify.post("/youtube", async (req, reply) => {
const { headers, body } = req;
const contentType = headers['content-type'];
// Check if the content type is 'application/atom+xml'
if (contentType === 'application/atom+xml') {
try {
// Parse the XML payload
console.log(JSON.stringify(body))
const feed = body["feed"];
// Example processing: log the video IDs of new videos
const entry = feed.entry[0];
if (!entry) return reply.code(200).send();
const [{ users: user, sites: site }] = await db.select().from(users).leftJoin(sites, eq(users.id, sites.user_id)).where(eq(users.channel_id, entry["yt:channelId"][0]));
if (!user || !site) throw new Error("User not found");
if (user.tokens < 3) throw new Error("Not enough tokens");
const videoId = entry["yt:videoId"][0];
const videoURL = `https://youtu.be/${videoId}`;
reply.code(200).send();
const video_data = await getVideoWithCaptions(videoURL);
const blog_content_json = await createBlogFromCaptions(video_data.captions, { title: video_data.title, description: video_data.description });
await db.insert(articles).values({
site_id: site.id,
title: blog_content_json.title,
content: blog_content_json.content,
meta_title: blog_content_json.meta_title,
meta_desc: blog_content_json.meta_desc,
excerp: blog_content_json.excerp,
source_video_id: videoId,
seo_slug: createArticleSlug(blog_content_json.title),
is_public: false
}).returning({ id: articles.id });
await db.update(users).set({
tokens: user.tokens - 3
}).where(eq(users.id, user.id));
// Respond with a success status
} catch (e) {
console.log(e)
return reply.code(500).send(e);
}
} else {
// Respond with an error status if the content type is not expected
return reply.code(400).send("Bad Request");
}
})
done();
};

View File

@ -1,218 +0,0 @@
const defaultModel = '@hf/mistralai/mistral-7b-instruct-v0.2';
import OpenAI, { toFile } from "openai";
import { env } from "./env.js";
import ytdl from "@distube/ytdl-core";
import { PassThrough } from "stream";
const openai = new OpenAI({
apiKey: env.OPENAI_TOKEN
});
async function cf_prompt(prompt, model = defaultModel) {
const options = {
method: 'POST',
headers: { Authorization: 'Bearer oJh-qUnkPmsaaE7mfor617dasBMnH_t9QFkSc5L2' },
body: '{ "messages": [{ "role": "system", "content": "You are a friendly assistant" }, { "role": "user", "content": "Why is pizza so good" }]}'
};
const res = await fetch('https://api.cloudflare.com/client/v4/accounts/f79c2f6c3ee16c813cbc853bc7e16166/ai/run/@hf/mistralai/mistral-7b-instruct-v0.2', options)
.then(response => response.json())
.catch(err => console.error(err));
console.log(res);
return res;
}
/**
*
* @param {import("openai/resources/index.mjs").ChatCompletionMessageParam[]} prompt
* @param {{model: string, is_json: boolean}} opts
* @returns
*/
async function promptGPT(prompt, { model, is_json } = { model: "gpt-3.5-turbo", is_json: true }) {
// return JSON.stringify({ "title": "Tech News Update: TikTok Ban, Snapdragon X Series CPUs, Tesla Troubles, and More", "body": "I was thinking about starting this video by singing the song Tik Tok by Kesha but I don't think anyone waking up in the morning feeling like P Diddy is a good vibe right now. The United States has officially passed a law banning TikTok next year if certain conditions aren't met. This ban is part of a larger foreign aid package in support of Ukraine and Israel. TikTok has vowed to fight the law in court, calling it an unconstitutional suppression of its American users' freedom of speech. In other tech news, Qualcomm has unveiled its upcoming lineup of Snapdragon X Series CPUs, including some confusing names. Tesla is facing troubles with its Cybertruck not being waterproof. And Google has delayed its plan to phase out third-party tracking cookies yet again. Stay tuned for more tech updates on TechLink!" });
openai.chat.completions.create({
model: model,
messages: prompt,
})
const options = {
method: 'POST',
headers: { Authorization: `Bearer ${env.OPENAI_TOKEN}`, "Content-Type": "application/json" },
body: JSON.stringify({
"model": "gpt-3.5-turbo",
// "max_tokens": 4096,
...(is_json ? { "response_format": { "type": "json_object" } } : {}),
"messages": prompt
})
};
console.log("prompting gpt-3.5-turbo...")
const res = await fetch('https://api.openai.com/v1/chat/completions', options)
.then(response => {
return response.json();
})
.catch(err => console.log(err));
console.log(res);
return res.choices[0].message.content;
}
/**
*
* @param {string} captions
* @param {{length: number, language: string, format: "summary"|"listicle"|"product review"|"tutorial", tone: "professional"|"informal"|"informational"}} param1
*
* @returns {Promise<{title: string, body: string, seo_friendly_slug: string}>}
*/
export async function createBlogFromCaptions(captions, {
title,
description
}, options = {}) {
const {
length,
language,
format,
tone,
faq
} = Object.assign({ length: 700, language: "English", format: "summary", tone: "informal", faq: false }, options);
// const prompt = `Convert the following video transcript into a blog post. The approximate length should be around ${length || 500} characters, written in ${language || "English"}. The desired format of the blog post is a ${format || "summary"}. Please ensure the blog post has a ${tone || "informal"} tone throughout. Use markdown to format the article. You must always respond in the following json fromat: {"title": string, "content": string, "seo_friendly_slug": string}. \nHere is the transcript: `
// const prompt = `Convert the following video transcript into an engaging blog post. You must always respond in the following json fromat: {"title": string, "body": string, "seo_friendly_slug": string}. Do not, under any circumstance, include the title inside the body, it should only be reserved for the body of the article. Use markdown to format the article. Use "\\n" to add line-breaks. \nHere is the transcript: `;
const basePrompt = [{
role: "system",
content: `# Role: SEO Content Specialist
## Profile:
- Author: Youpage.ai
- Version: 1.0
- Language: English
- Description: An SEO Content Specialist is an expert in creating high-quality, factually correct, and search engine optimized content. This role involves transforming transcripts, drafts, or raw content into polished articles that rank well on search engines and engage readers.
### Skill:
1. Expertise in SEO best practices.
2. Knowledge of Markdown formatting and how to use it to improve readability.
3. Proficiency in content writing and editing.
4. Strong understanding of keyword research and usage.
5. Ability to maintain factual accuracy.
6. Knowledge of news article formatting and structure.
## Goals:
1. Create a high-quality news article from the provided transcript.
2. Ensure the article is SEO optimized.
3. Maintain factual accuracy throughout the article.
4. Enhance readability and engagement.
5. Use relevant keywords effectively.
## Constraints VERY IMPORTANT:
1. Do not alter the factual content from the transcript.
2. Follow SEO best practices.
3. Ensure the article is engaging and readable.
4. Adhere to news article formatting.
5. Limit the use of adjectives. Try to use precise nouns and verbs instead.
6. Use unique descriptions and avoid repeating the same phrases.
7. Vary the vocabulary to avoid repetition.
8. Keep sentences concise to maintain pace and tension.
9. Ensure a consistent perspective and unique character voice.
10. Do NOT use this sentence structure: [[Subject] [Predicate] [Object] [Appositive] [Prepositional Phrase] [Relative Clause]]
11. Do NOT use these phrases: delve, Cosmic, cosmos, heady, void, stars, testament, hum, palpable, symphony, tapestry, woven, complemented, grim, I can't deny, reminder, stark, contrast, storm, tempest, a far cry, an echo, navigate, sheepish.
## OutputFormat:
1. Do not include a title at the top of the article.
2. VERY IMPORTANT! Use markdown to add formatting to the article and make it easier to read.
3. Length: The article should be roughly ${length || 1200} words.
${faq ? "4. FAQ Section: Add a FAQ section at the end of the article to address common questions." : ""}
## Workflow:
1. First, analyze the provided transcript to understand the key points and factual content.
2. Then, conduct keyword research to identify relevant and high-ranking keywords for the article.
3. Next, draft a compelling headline and introduction that include the main keyword.
4. After that, write the body of the article, ensuring it is factually correct, well-structured, and around {{word_count}} words in length.
5. Conclude the article with a strong closing and ensure all SEO elements are in place, including meta description and alt text for any images.
${faq ? "6. Finally, add an FAQ section at the end of the article to address common questions related to the topic." : ""}
## Initialization:
As an SEO Content Specialist, you must follow the specified rules and communicate in English. Respond only with the article content.`
}, {
role: "user",
content: `# Information:
## Title
${title}
## Transcript
${captions}`
}];
let response = await openai.chat.completions.create({
model: "gpt-4o",
messages: basePrompt
});
console.log("first response", response.choices[0].message.content)
let final = await openai.chat.completions.create({
model: "gpt-3.5-turbo",
messages: [...basePrompt, { "role": "assistant", "content": response.choices[0].message.content }, { "role": "user", "content": "Respond only in JSON with the following format: { meta_title: string, meta_desc: string, slug: string, excerp: string, title: string }" }],
response_format: { type: "json_object" }
})
console.log("second response", final.choices[0].message.content)
let result = Object.assign(JSON.parse(final.choices[0].message.content), {
content: response.choices[0].message.content,
})
return result;
}
function wordsToTokens(n) {
return Math.ceil(n / 0.75);
}
export async function getWhisperCaptions(video_url) {
const audio = await getYouTubeAudioBuffer(video_url);
const file = await toFile(audio, "audio.mp3");
const res = await openai.audio.transcriptions.create({
file: file,
model: "whisper-1",
}).catch(x => console.log(x));
return res.text;
}
/**
* Fetches the audio data from a YouTube video URL and returns it as a buffer.
* @param {string} videoUrl - The YouTube video URL.
* @returns {Promise<Buffer>} A promise that resolves with the audio data as a buffer.
*/
function getYouTubeAudioBuffer(videoUrl) {
return new Promise((resolve, reject) => {
if(!videoUrl) reject();
// Download audio from YouTube
const audioStream = ytdl(videoUrl, { filter: "audioonly", quality: "lowestaudio" });
// Create a PassThrough stream to hold the audio data in memory
const passThrough = new PassThrough();
// Pipe the audio stream to the PassThrough stream
audioStream.pipe(passThrough);
// Collect the audio data into a buffer
let audioDataBuffer = [];
// Listen for data events on the PassThrough stream
passThrough.on('data', chunk => {
audioDataBuffer.push(chunk);
});
// Listen for end event to indicate the stream has finished
passThrough.on('end', () => {
// Convert the collected chunks into a single Buffer
const audioBufferData = Buffer.concat(audioDataBuffer);
// Resolve the promise with the audio data buffer
resolve(audioBufferData);
});
// Listen for error event on the PassThrough stream
passThrough.on('error', err => {
// Reject the promise if an error occurs
reject(err);
});
});
}

View File

@ -1,50 +0,0 @@
import { eq } from "drizzle-orm";
import { db } from "../db/index.js";
import { sites } from "../db/schemas.js";
import nodemailer from 'nodemailer';
const TOKEN = "a0cc97f4f856d6c16d70bb5984e32cef";
const ENDPOINT = "https://send.api.mailtrap.io/";
const default_sender = "mailtrap@demomailtrap.com";
var client = nodemailer.createTransport({
host: "live.smtp.mailtrap.io",
port: 587,
auth: {
user: "api",
pass: "a0cc97f4f856d6c16d70bb5984e32cef"
}
});
export async function sendContactEmail(recipient, sender, subject, body) {
client.sendMail({
from: default_sender,
to: recipient,
subject: `YouPage.ai: ${subject}`,
text: `You got a new message from your Youpage.ai blog!\nSender: ${sender}\nMessage: ${body}`
}).catch(console.error);
}
export async function sendFreebie(recipient, blog_id) {
const [blog] = await db.select().from(sites).where(eq(sites.id, blog_id));
if (!blog) throw new Error("Invalid site");
if (!blog.use_freebie || !blog.freebie_url) return;
const sender = default_sender;
const recipients = [
{
email: recipient,
}
];
client
.sendMail({
from: sender,
to: recipients,
subject: `Your freebie from ${blog.name}`,
text: `Thank you for subscribing to ${blog.name}! Your freebie is linked below.\n ${blog.freebie_url}`,
}).catch(console.error);
}

View File

@ -6,16 +6,6 @@ const envSchema = z.object({
REDIS_URL: z.string().default("redis://127.0.0.1:6379/"), REDIS_URL: z.string().default("redis://127.0.0.1:6379/"),
PORT: z.coerce.number().default(8080), PORT: z.coerce.number().default(8080),
HOST: z.string().default("127.0.0.1"), HOST: z.string().default("127.0.0.1"),
PUBLIC_API_URL: z.string(),
FRONTEND_URL: z.string(),
SITES_HOST: z.string(),
GOOGLE_CLIENT_ID: z.string(),
GOOGLE_SECRET: z.string(),
OPENAI_TOKEN: z.string(),
CERTS_URL: z.string().nullable().default(null),
STRIPE_KEY: z.string(),
STRIPE_WEBHOOK_SECRET: z.string(),
STRIPE_SUBSCRIPTION_PRICE_ID: z.string(),
}); });
export const env = envSchema.parse(process.env); export const env = envSchema.parse(process.env);

View File

@ -1,75 +1,3 @@
export * from "./env.js"; export * from "./env.js";
export * from "./logger.js"; export * from "./logger.js";
export * from "./redis.js"; export * from "./redis.js";
export * from "./ai.js";
export * from "./token.js";
export * from "./youtube.js";
/**
*
* @param {string} title
* @returns {string}
*/
export function createArticleSlug(title) {
const maxLength = 20;
const slug = title.toLowerCase().replace(/[^a-z0-9\s-]/g, '').trim().replace(/\s+/g, '-');
const randomChars = "-" + generateRandomChars(6);
// If slug is shorter than or equal to 12 characters, return it directly
if (slug.length <= maxLength) {
return slug + randomChars;
}
// Find the last space within the first 12 characters
const lastSpaceIndex = slug.substring(0, maxLength).lastIndexOf('-');
// If no space found, truncate slug to 12 characters
const truncatedSlug = lastSpaceIndex !== -1 ? slug.substring(0, lastSpaceIndex) : slug.substring(0, maxLength);
// Generate 6 random characters
// Concatenate the truncated slug and random characters
const finalSlug = truncatedSlug + randomChars;
return finalSlug;
}
function generateRandomChars(length) {
let result = '';
const characters = 'abcdefghijklmnopqrstuvwxyz0123456789';
for (let i = 0; i < length; i++) {
result += characters.charAt(Math.floor(Math.random() * characters.length));
}
return result;
}
/**
* Convert a JSON object to a valid CSV string
* @param {JSON[]} jsonData
* @returns {string} CSV Data
*/
export function jsonToCsv(jsonData) {
// Extract headers from the first object in the JSON array
const headers = Object.keys(jsonData[0]);
// Join headers to form the first line of CSV
let csv = headers.map(header => `"${header}"`).join(',') + '\n';
// Loop through each object in the JSON array
for (let obj of jsonData) {
// Extract values corresponding to each header
const values = headers.map(header => {
let value = obj[header];
// Check if the value contains a comma, if so, wrap it in double quotes
if (typeof value === 'string' && value.includes(',')) {
value = `"${value}"`;
}
return value;
});
// Join values with comma separator
csv += values.join(',') + '\n';
}
return csv;
}

View File

@ -1,6 +0,0 @@
import * as Sentry from "@sentry/node";
// Ensure to call this before importing any other modules!
Sentry.init({
dsn: "https://0092593709894b0eaec1702e0136594b@glitchtip.omersabic.com/4",
});

View File

@ -1,107 +0,0 @@
import loadStripe from 'stripe';
import { env } from './env.js';
import { db } from '../db/index.js';
import { users } from '../db/schemas.js';
import { eq, sql } from 'drizzle-orm';
const stripe = await loadStripe(env.STRIPE_KEY);
/**
*
* @param {string} email
* @param {string} name
* @returns {import('stripe-v3').customer.create}
*/
export async function createCustomer(email, name) {
return (await stripe.customers.create({
email: email,
name: name,
}));
}
/**
*
* @param {string} stripe_id
*/
export async function getBillingDashboard(stripe_id, return_url) {
const session = await stripe.billingPortal.sessions.create({
customer: stripe_id,
return_url
});
return session.url;
}
export async function createCheckout(price_id, customer_id, metadata = {}, subscription_data = {}) {
const session = await stripe.checkout.sessions.create({
mode: 'subscription',
customer: customer_id,
line_items: [
{
price: price_id,
quantity: 1
},
],
metadata,
subscription_data,
success_url: env.FRONTEND_URL + '/payments/success?session_id={CHECKOUT_SESSION_ID}',
cancel_url: env.FRONTEND_URL,
});
return session.url;
}
export async function handleWebhook(body, signature) {
let data;
let eventType;
// Check if webhook signing is configured.
const webhookSecret = env.STRIPE_WEBHOOK_SECRET;
if (webhookSecret) {
// Retrieve the event by verifying the signature using the raw body and secret.
let event;
try {
event = stripe.webhooks.constructEvent(
body,
signature,
webhookSecret
);
} catch (err) {
console.log(err);
console.log(`⚠️ Webhook signature verification failed.`);
throw new Error("Verification failed")
}
// Extract the object from the event.
data = event.data;
eventType = event.type;
} else {
// Webhook signing is recommended, but if the secret is not configured in `config.js`,
// retrieve the event data directly from the request body.
data = body.data;
eventType = body.type;
}
switch (eventType) {
case 'invoice.paid':
console.log("invoice paid!")
const [user] = await db.select().from(users).where(eq(users.stripe_id, data.object.customer));
console.log(data);
if (!user) throw new Error("Failed to get user ID");
let currentDate = new Date();
let subscriptionEndDate = new Date(currentDate);
if(user.trial_used) {
subscriptionEndDate.setMonth(currentDate.getMonth() + 1);
} else {
subscriptionEndDate.setDate(currentDate.getDate() + 14);
}
await db.update(users).set({
subscribed_until: subscriptionEndDate,
subscription_tier: "basic",
tokens: sql`${users.tokens} + 25`,
trial_used: true
}).where(eq(users.stripe_id, data.object.customer));
break;
default:
break;
}
}

View File

@ -1,44 +0,0 @@
import { eq } from "drizzle-orm";
import { db } from "../db/index.js";
import { sessions } from "../db/schemas.js";
/**
*
* @param {string} user_id User to assign the auth token to
* @param {{access_token: string, refresh_token: string, expires_at: Date}} googleAuth
*
* @returns {Promise<{session_id: string}>} Auth token
*/
export async function createSession(user_id, {
access_token,
refresh_token,
expires_at,
}, tx = db) {
try {
const [existing_session] = await tx.select().from(sessions).where(eq(sessions.user_id, user_id));
if (existing_session) {
return {
session_id: existing_session.id
}
}
const token = await tx.insert(sessions).values({
user_id,
access_token: access_token,
refresh_token: refresh_token,
expires_at
}).returning({ id: sessions.id });
if (token.length == 0) {
throw new Error("Error creating auth token");
}
return {
session_id: token[0].id
}
} catch (e) {
console.log(e);
throw e;
}
}

View File

@ -1,520 +0,0 @@
import { eq } from 'drizzle-orm';
import { db } from '../db/index.js';
import { sessions } from '../db/schemas.js';
import { google } from 'googleapis';
import ytdl from "@distube/ytdl-core";
import { getWhisperCaptions } from './ai.js';
import { articles as articlesTable } from "../db/schemas.js";
import * as xml2js from 'xml2js';
const service = google.youtube("v3");
const agent = ytdl.createAgent([
{
"domain": ".youtube.com",
"expirationDate": 1757885754.678493,
"hostOnly": false,
"httpOnly": false,
"name": "__Secure-1PAPISID",
"path": "/",
"sameSite": "unspecified",
"secure": true,
"session": false,
"storeId": "1",
"value": "Ydllg_a0ziZ2IRkW/AxTe18r6nUqk7BEbQ",
"id": 1
},
{
"domain": ".youtube.com",
"expirationDate": 1757885754.678415,
"hostOnly": false,
"httpOnly": true,
"name": "__Secure-1PSID",
"path": "/",
"sameSite": "unspecified",
"secure": true,
"session": false,
"storeId": "1",
"value": "g.a000mwiFpDXHv-mE1JbzAv29Ye5X1D7V-zT1tEGx1_bd46Kree9yTMpJEGK4nnajwLM5d0hj7gACgYKAYUSARUSFQHGX2MiVlSE8GgO3Vmj3YT3n8k6WhoVAUF8yKoU4xPY7CM6zb7j98zdaVV70076",
"id": 2
},
{
"domain": ".youtube.com",
"expirationDate": 1754861761.153214,
"hostOnly": false,
"httpOnly": true,
"name": "__Secure-1PSIDCC",
"path": "/",
"sameSite": "unspecified",
"secure": true,
"session": false,
"storeId": "1",
"value": "AKEyXzXzGpG2-NgDLDoR0-TXlyJVUQJQ_hzs2wEvAZmPy5VTxaOIXg4QrrR3N_v5EWUqhQ4q",
"id": 3
},
{
"domain": ".youtube.com",
"expirationDate": 1754861754.678387,
"hostOnly": false,
"httpOnly": true,
"name": "__Secure-1PSIDTS",
"path": "/",
"sameSite": "unspecified",
"secure": true,
"session": false,
"storeId": "1",
"value": "sidts-CjIBUFGohzuXoAbjBYmkalmpToyVGhPcAMyp-r3IgiyU7WlYnluERYQWrB-zOvpwcD5F_RAA",
"id": 4
},
{
"domain": ".youtube.com",
"expirationDate": 1757885754.678506,
"hostOnly": false,
"httpOnly": false,
"name": "__Secure-3PAPISID",
"path": "/",
"sameSite": "no_restriction",
"secure": true,
"session": false,
"storeId": "1",
"value": "Ydllg_a0ziZ2IRkW/AxTe18r6nUqk7BEbQ",
"id": 5
},
{
"domain": ".youtube.com",
"expirationDate": 1757885754.678427,
"hostOnly": false,
"httpOnly": true,
"name": "__Secure-3PSID",
"path": "/",
"sameSite": "no_restriction",
"secure": true,
"session": false,
"storeId": "1",
"value": "g.a000mwiFpDXHv-mE1JbzAv29Ye5X1D7V-zT1tEGx1_bd46Kree9y4tCIp7vBEBU5tBLxdPiklQACgYKAVgSARUSFQHGX2MiCFYG1JWlH9dAu8gduZa6dRoVAUF8yKpMaD9-H5JRU8a-L-_Za4wd0076",
"id": 6
},
{
"domain": ".youtube.com",
"expirationDate": 1754861761.153238,
"hostOnly": false,
"httpOnly": true,
"name": "__Secure-3PSIDCC",
"path": "/",
"sameSite": "no_restriction",
"secure": true,
"session": false,
"storeId": "1",
"value": "AKEyXzUy-4CY7B0Gu-XHjrZVVpAWPNJNET5PQOlbRbmaa_ecvp7AS8Nwf5gbDTfL47zj0Dez3Q",
"id": 7
},
{
"domain": ".youtube.com",
"expirationDate": 1754861754.678402,
"hostOnly": false,
"httpOnly": true,
"name": "__Secure-3PSIDTS",
"path": "/",
"sameSite": "no_restriction",
"secure": true,
"session": false,
"storeId": "1",
"value": "sidts-CjIBUFGohzuXoAbjBYmkalmpToyVGhPcAMyp-r3IgiyU7WlYnluERYQWrB-zOvpwcD5F_RAA",
"id": 8
},
{
"domain": ".youtube.com",
"expirationDate": 1757453723.663338,
"hostOnly": false,
"httpOnly": true,
"name": "__Secure-YEC",
"path": "/",
"sameSite": "lax",
"secure": true,
"session": false,
"storeId": "1",
"value": "CgtVakdtMnJrZ2VwRSi_ut-1BjIKCgJERRIEEgAgKQ%3D%3D",
"id": 9
},
{
"domain": ".youtube.com",
"expirationDate": 1757885754.678463,
"hostOnly": false,
"httpOnly": false,
"name": "APISID",
"path": "/",
"sameSite": "unspecified",
"secure": false,
"session": false,
"storeId": "1",
"value": "PzwvCMFIwi-WXHNK/Aa7aeSu6UGGiR3Xw-",
"id": 10
},
{
"domain": ".youtube.com",
"expirationDate": 1723326359,
"hostOnly": false,
"httpOnly": false,
"name": "CONSISTENCY",
"path": "/",
"sameSite": "unspecified",
"secure": true,
"session": false,
"storeId": "1",
"value": "AKreu9uoPRzjfNbvjscAvYa4AnZ7yXSd9GkhniF8n1uodA2SI5_UawxviB_l5efAOS7TIHbrzFvCUM3riEdLFWvDQ0So_SGThxdrORfZ0vAZzDn30MIgE0ddEa_h-aBhNzX4ispknt4SY4E76opMTGsu",
"id": 11
},
{
"domain": ".youtube.com",
"expirationDate": 1757885754.678441,
"hostOnly": false,
"httpOnly": true,
"name": "HSID",
"path": "/",
"sameSite": "unspecified",
"secure": false,
"session": false,
"storeId": "1",
"value": "An0F5SiZ_NJH4_Rx4",
"id": 12
},
{
"domain": ".youtube.com",
"expirationDate": 1757885756.324828,
"hostOnly": false,
"httpOnly": true,
"name": "LOGIN_INFO",
"path": "/",
"sameSite": "no_restriction",
"secure": true,
"session": false,
"storeId": "1",
"value": "AFmmF2swRQIgQD5euCkX56qyeRocOC192DF6siCwmZiG5RFdXIsIOKQCIQDVYMUXa9fafpFC4gJAi32F-LsqO7a5ptBfytQ0ooI-7g:QUQ3MjNmd1RmUlRfd25hTUFDT214TF9pcHlXcEh5ZHAzc2d0Rjc2WjZVUEJCRW1KSV9QRTlBZXpTLXA1QXFtdExyMzFncVdteTE4N1pMWk9yUnhTVU8taF93eEZlRm95UTdubHJ3emt4SVBYeVNjQ0dDREtnb2JWeHhHeU1HYy1xVlhlZTU4V3g3TnEyMm9uWDZzTzlBZHhjSG81dXZ3WnJn",
"id": 13
},
{
"domain": ".youtube.com",
"expirationDate": 1739136959.800355,
"hostOnly": false,
"httpOnly": true,
"name": "NID",
"path": "/",
"sameSite": "unspecified",
"secure": true,
"session": false,
"storeId": "1",
"value": "516=aE2sFI_rh_Rh7q0T-GD9vzWm4pf6Uak1iFjvzBFuRr8wxfSmJEDS6NzXpXKLLNKi_ba-NaxkIxkfhKJ9I3ODz9TmFnuTwQVvGbSordWz2DI3IvtXEygFNEGTTW1WuKBOHO-WXjdM2nuQ4b7d3KWiDttthySlnE6l0vur98aENPo",
"id": 14
},
{
"domain": ".youtube.com",
"expirationDate": 1757885758.099494,
"hostOnly": false,
"httpOnly": false,
"name": "PREF",
"path": "/",
"sameSite": "unspecified",
"secure": true,
"session": false,
"storeId": "1",
"value": "f6=40000000&tz=Europe.Berlin",
"id": 15
},
{
"domain": ".youtube.com",
"expirationDate": 1757885754.678475,
"hostOnly": false,
"httpOnly": false,
"name": "SAPISID",
"path": "/",
"sameSite": "unspecified",
"secure": true,
"session": false,
"storeId": "1",
"value": "Ydllg_a0ziZ2IRkW/AxTe18r6nUqk7BEbQ",
"id": 16
},
{
"domain": ".youtube.com",
"expirationDate": 1757885754.678333,
"hostOnly": false,
"httpOnly": false,
"name": "SID",
"path": "/",
"sameSite": "unspecified",
"secure": false,
"session": false,
"storeId": "1",
"value": "g.a000mwiFpDXHv-mE1JbzAv29Ye5X1D7V-zT1tEGx1_bd46Kree9yaWb-XRQACGvVe52ZscToXgACgYKAc4SARUSFQHGX2MipK_BxY_xGkd82ZuR_-usghoVAUF8yKoD-HK1Ms_BeaLioA-9grr10076",
"id": 17
},
{
"domain": ".youtube.com",
"expirationDate": 1754861761.153147,
"hostOnly": false,
"httpOnly": false,
"name": "SIDCC",
"path": "/",
"sameSite": "unspecified",
"secure": false,
"session": false,
"storeId": "1",
"value": "AKEyXzVET2aVXEooYcLzVZcKqrNU4ugR61hBJSb9_Kg9YQSdRmMSiwZNQZEHDNA0spao7jlE",
"id": 18
},
{
"domain": ".youtube.com",
"expirationDate": 1757453733.489585,
"hostOnly": false,
"httpOnly": false,
"name": "SOCS",
"path": "/",
"sameSite": "lax",
"secure": true,
"session": false,
"storeId": "1",
"value": "CAESFggDEgk2NjA1NTYzMjIaBWVuLUdCIAEaBgiA29q1Bg",
"id": 19
},
{
"domain": ".youtube.com",
"expirationDate": 1757885754.678452,
"hostOnly": false,
"httpOnly": true,
"name": "SSID",
"path": "/",
"sameSite": "unspecified",
"secure": true,
"session": false,
"storeId": "1",
"value": "AMLcUMa2BaB8t4bo0",
"id": 20
},
{
"domain": ".youtube.com",
"expirationDate": 1723325764,
"hostOnly": false,
"httpOnly": false,
"name": "ST-hcbf8d",
"path": "/",
"sameSite": "unspecified",
"secure": false,
"session": false,
"storeId": "1",
"value": "session_logininfo=AFmmF2swRQIgQD5euCkX56qyeRocOC192DF6siCwmZiG5RFdXIsIOKQCIQDVYMUXa9fafpFC4gJAi32F-LsqO7a5ptBfytQ0ooI-7g%3AQUQ3MjNmd1RmUlRfd25hTUFDT214TF9pcHlXcEh5ZHAzc2d0Rjc2WjZVUEJCRW1KSV9QRTlBZXpTLXA1QXFtdExyMzFncVdteTE4N1pMWk9yUnhTVU8taF93eEZlRm95UTdubHJ3emt4SVBYeVNjQ0dDREtnb2JWeHhHeU1HYy1xVlhlZTU4V3g3TnEyMm9uWDZzTzlBZHhjSG81dXZ3WnJn",
"id": 21
},
{
"domain": ".youtube.com",
"expirationDate": 1723325763,
"hostOnly": false,
"httpOnly": false,
"name": "ST-l3hjtt",
"path": "/",
"sameSite": "unspecified",
"secure": false,
"session": false,
"storeId": "1",
"value": "session_logininfo=AFmmF2swRQIgQD5euCkX56qyeRocOC192DF6siCwmZiG5RFdXIsIOKQCIQDVYMUXa9fafpFC4gJAi32F-LsqO7a5ptBfytQ0ooI-7g%3AQUQ3MjNmd1RmUlRfd25hTUFDT214TF9pcHlXcEh5ZHAzc2d0Rjc2WjZVUEJCRW1KSV9QRTlBZXpTLXA1QXFtdExyMzFncVdteTE4N1pMWk9yUnhTVU8taF93eEZlRm95UTdubHJ3emt4SVBYeVNjQ0dDREtnb2JWeHhHeU1HYy1xVlhlZTU4V3g3TnEyMm9uWDZzTzlBZHhjSG81dXZ3WnJn",
"id": 22
},
{
"domain": ".youtube.com",
"expirationDate": 1723325763,
"hostOnly": false,
"httpOnly": false,
"name": "ST-tladcw",
"path": "/",
"sameSite": "unspecified",
"secure": false,
"session": false,
"storeId": "1",
"value": "session_logininfo=AFmmF2swRQIgQD5euCkX56qyeRocOC192DF6siCwmZiG5RFdXIsIOKQCIQDVYMUXa9fafpFC4gJAi32F-LsqO7a5ptBfytQ0ooI-7g%3AQUQ3MjNmd1RmUlRfd25hTUFDT214TF9pcHlXcEh5ZHAzc2d0Rjc2WjZVUEJCRW1KSV9QRTlBZXpTLXA1QXFtdExyMzFncVdteTE4N1pMWk9yUnhTVU8taF93eEZlRm95UTdubHJ3emt4SVBYeVNjQ0dDREtnb2JWeHhHeU1HYy1xVlhlZTU4V3g3TnEyMm9uWDZzTzlBZHhjSG81dXZ3WnJn",
"id": 23
},
{
"domain": ".youtube.com",
"expirationDate": 1723325764,
"hostOnly": false,
"httpOnly": false,
"name": "ST-xuwub9",
"path": "/",
"sameSite": "unspecified",
"secure": false,
"session": false,
"storeId": "1",
"value": "session_logininfo=AFmmF2swRQIgQD5euCkX56qyeRocOC192DF6siCwmZiG5RFdXIsIOKQCIQDVYMUXa9fafpFC4gJAi32F-LsqO7a5ptBfytQ0ooI-7g%3AQUQ3MjNmd1RmUlRfd25hTUFDT214TF9pcHlXcEh5ZHAzc2d0Rjc2WjZVUEJCRW1KSV9QRTlBZXpTLXA1QXFtdExyMzFncVdteTE4N1pMWk9yUnhTVU8taF93eEZlRm95UTdubHJ3emt4SVBYeVNjQ0dDREtnb2JWeHhHeU1HYy1xVlhlZTU4V3g3TnEyMm9uWDZzTzlBZHhjSG81dXZ3WnJn",
"id": 24
}
]);
export async function getVideoById(access_token, video_id) {
const video = await service.videos.list({
access_token,
id: video_id,
part: "snippet"
}).then(x => x.data.items[0].snippet);
return video;
}
/**
*
* @param {string} access_token
* @param {string} playlist_id
*
* @returns {Promise<import("googleapis").youtube_v3.Schema$PlaylistItem[]>}
*/
export async function getVideosFromPlaylist(access_token, playlist_id) {
if (!access_token) return [];
let videos;
try {
videos = await service.playlistItems.list({
access_token,
part: "id,snippet",
playlistId: playlist_id,
maxResults: 10
}).then(res => res)
} catch (e) {
return [];
}
console.log(videos.status);
if (videos.status > 299 || videos.status < 200) {
return [];
}
return videos.data.items;
}
/**
*
* @param {string} access_token
*
* @returns {Promise<import("googleapis").youtube_v3.Schema$Channel>}
*/
export async function getChannelInfo(access_token) {
const channel = await service.channels.list({
access_token,
part: "id,contentDetails,snippet",
mine: true,
maxResults: 1
}).then(res => res);
if (!channel.data.items[0]) {
throw new Error("no_channel");
}
return channel.data.items[0];
}
/** @typedef {{name: string, email: string, isVerified: boolean}} UserInfo */
/**
*
* @param {string} access_token
*
* @returns {Promise<UserInfo>}
*/
export async function getUserInfo(access_token) {
const url = "https://www.googleapis.com/oauth2/v1/userinfo?alt=json";
// const url = "https://people.googleapis.com/v1/people/me?personFields=names,emailAddresses";
const response = await fetch(url, {
headers: {
"Authorization": `Bearer ${access_token}`
}
})
const data = await response.json();
return data
}
/**
*
* @param {import('fastify').FastifyInstance} fastify
* @param {import('fastify').FastifyRequest} request
*
* @returns {Promise<string>}
*/
export async function getAccessToken(fastify, request) {
// TODO: Move to cache instead of postgres
// const [cachedToken] = await db.select().from(sessions).where(eq(sessions.id, request.session.id));
let access_token = request.session.access_token;
if ((new Date().getTime() + 10) > request.session.expires_at) {
/** @type {import('@fastify/oauth2').Token} */
const { token } = await fastify.googleOAuth2.getNewAccessTokenUsingRefreshToken(request.session).catch(e => ({ token: null }));
if (!token) return ""
access_token = token.access_token;
await db.update(sessions).set({
expires_at: token.expires_at,
access_token: token.access_token
}).where(eq(sessions.user_id, request.session.user_id));
}
return access_token;
}
export async function getVideoDetails(video_url) {
if (!(ytdl.validateURL(video_url))) throw new Error("Invalid Youtube URL");
const info = await ytdl.getBasicInfo(video_url, {agent});
return {
title: info.videoDetails.title,
description: info.videoDetails.description
}
}
export function parseTextFromCaptions(caption_text) {
let text_content = "";
const captionEntries = caption_text.split(/\n\n/);
for (const entry of captionEntries) {
const lines = entry.trim().split('\n');
if (lines.length >= 2 && !lines[1].includes('-->')) {
text_content += lines.slice(1).join(' ').trim() + ' ';
}
}
return text_content
}
function parseXMLCaptions(xmlString) {
return new Promise((resolve, reject) => {
const parser = new xml2js.Parser();
parser.parseString(xmlString, (err, result) => {
if (err) {
reject(err);
} else {
const textElements = result.transcript.text;
let captionText = textElements.map(element => element._).join('\n');
resolve(captionText.trim());
}
});
});
}
function delay(time) {
return new Promise(resolve => setTimeout(resolve, time));
}
export async function getVideoWithCaptions(video_url) {
const info = await ytdl.getInfo(video_url, {agent});
const tracks = info
.player_response.captions
.playerCaptionsTracklistRenderer.captionTracks;
if (!tracks || tracks.length < 1) throw new Error("no_captions");
const track = tracks.find(t => t.languageCode == "en");
if(!track) throw new Error("no_captions");
const captions = await (await fetch(`${track.baseUrl}&fmt=srv1`)).text();
return {
title: info.videoDetails.title,
description: info.videoDetails.description,
captions: await parseXMLCaptions(captions)
}
}

324
test.md
View File

@ -1,324 +0,0 @@
last week AMD launched their new
graphics cards and I already reviewed
the RX 7900 XTX so today I'm going to
talk about this RX 7900 XT now I did not
have a chance to test the reference card
but I do have this tough gaming model
from Asus and while I do think that AMD
has done a great job at creating
high-end graphics cards and bringing
some well needed competition to the
market I also think that there are
plenty of things that still need a bit
of work and that should be improved so
I'm going to take a bit of a different
approach today and I'm going to talk
about my issues with this 7900 XT let's
go this video is brought to you by
Corsair and their brand new Dominator
Platinum RGB memory these super fast
ddr5 6000 Hertz memory kits are
specifically made for ryzen 7000 series
CPUs they feature a stylish aluminum
heat spreader with dhx technology that
keeps them nice and cool under load
offering a smooth and stable performance
with a lot out of room for overclocking
and they come with 12 customizable
kapelix LEDs that you can control with
their IQ software and easily sync up
with your other coarser components check
them out using the links in the
description below one of the first and
most obvious issues I had with this card
is its name the RX 7900 XT sounds very
similar to the 7900 XTX even though when
we look at the specs you can see that
they are vastly different the XCX has
more cores higher clock speeds more
memory and a bigger memory bus than the
XT and I don't think that removing One X
at the end of the product name is enough
to distinguish and obviously lower tier
card from a higher tier one it just
causes a lot of confusion especially for
the less informed consumers that might
think that they are buying a very
similar product for a hundred dollars
less which is definitely not the case
here now Nvidia received a lot of
criticism for their 4086 16 gig and 4080
12 gig cards and AMD here is pretty much
doing the same thing the XT is more than
15 slower than the xdx and they should
have called it a 7800 XT or at least a
7900 without any X's just to make it a
bit more obvious to everyone and that
brings me to my next issue the relative
performance because the XT is priced so
closely to the XTX its performance
should also be at a close distance
compared to the xdx and that is
definitely not the case here so the xt's
recommended price is only 10 lower than
the xdx but when it comes to Performance
the XTX is 16 faster than the XD and
keep in mind I was using the relatively
fast third-party version of the XT so
for 100 more you can get a card that is
16 faster and if you already decided to
spend 900 on a GPU upgrading to dxdx for
a hundred dollars more just seems like a
logical choice and that pressure from
the relative performance is a bit of a
shame because in absolute terms the XT
is a pretty powerful chip at 1440p
resolution it shows a great High refresh
rate experience in every game I've
tested and most games will easily run at
144 FPS or above but even the heaviest
titles like Dying Light 2 and cyberpunk
2077 for example will get pretty close
to that at 4K resolution the XC does
well offering that 4K 120 experience in
lighter titles and 60-ish FPS or above
in heavier games amd's FSR has also been
adopted by a lot of game developers so
for the titles that do support it this
7900 XT is actually a very capable 4K
gaming card as well but should you buy a
much stronger XTX for 100 more
absolutely now the next issue I had was
performance inconsistency when testing
Call of Duty Modern Warfare we were
getting really inconsistent results so
one moment the XT would be running at
around 2400 megahertz showing FPS
numbers that kind of fall in line with
other cards but during some other runs
it would suddenly boost much higher
somewhere closer to 2 900 megahertz
which would result in a much higher
average FPS and which would put it above
the RTX 4090 and the 7900 XDS and there
was absolutely no logic behind it so it
was the same system same settings no
updates in between the CPU wasn't
throttling or anything else that we can
think of actually so every time we
reboot the PC it would act differently
for no clear reason now in this
particular game it doesn't really matter
as much because in the worst case
scenario it was close to an RTX 4090 and
in the best case scenario it was
actually beating it which is definitely
not bad for a GPU that costs half as
much but seeing this inconsistent
Behavior always causes doubt whether
everything is working well as it should
and what kind of an impact does it have
in titles where this card was not doing
as well but AMD has been on top of this
since I mentioned it and they are trying
to figure it out but there are no clear
answers just yet now hopefully they will
come up with a driver update that will
stabilize this situation a bit more now
my next issue is a really noticeable one
coil line in my review of the XTX I
mentioned that my reference card had
some coil line now coil wine is mostly
sample specific and then it is often
specific to certain games or super high
FPS gameplay but the XT here had a
noticeable amount of coal wine regularly
and since the launch a lot of reviewers
as well as people who bought their cards
frequently mentioned coil wine too both
in reference cards as well as third
party cards so this tough gaming from
Asus doesn't always whine but it does
sit pretty frequently and since the
sound often changes depending on what's
going on with your PC it is way more
noticeable than if it was just a
consistent tone I did manage to test the
thermal and noise performance at one
point where it wasn't whining and this
oversized tough cooler performed
extremely well barely going over 50
degrees with almost no noise at all and
even though this should be a great
cooler design it is only quiet sometimes
the other times it is just super whiny
and given the fact that these reports
are so widespread with all sorts of
Radeon cards it just feels like this is
something that AMD really needs to work
on my next issue is the power
consumption especially in idle when
having multiple monitors connected to
your cart if I just connect a single
monitor power consumption is completely
fine with a single watt of a difference
between the 4080 and this 7900 XD but
when connecting two monitors so I had
one 4K 120 hertz one and one 4K 60hz one
the power consumption on the XT went up
by a lot it was 15 watts higher than the
4080 and I would say that is a
significant change now depending on how
much you pay for your electricity and
how many hours you have your monitors on
per day this could really add up quickly
now supposedly this should get fixed
with the next driver so I will put an
update in the comment under this video
once I learn more about this
but perhaps the biggest issue right now
is the supply and pricing before these
cards launch there were plenty of posts
saying that there should be plenty of
stock and that the prices should be as
promised but in reality the supply so
far hasn't been as good it will vary per
region but if I look at the Netherlands
I haven't seen a single xdx listed at
any reputable retailer and even the XT
which had more critical reviews overall
has been really hard to find most shops
I spoke to told me that they have no
clear indication of stock on any Radeon
cards at this point and the couple shops
that did list the XT seem to have very
limited stock and they're selling them
at very high prices so right now the
cheapest one I can find is at 1300 Euros
which is 250 euros over the MSRP and
only about 150 Euros less than a 4080
and a 4080 is faster has stronger Ray
tracing performance and it is more power
efficient and I think all this was much
worse because of the hype that followed
the AMD launch and the belief that there
might actually be some stock or that the
prices might actually stay down anyway
the launch was definitely a bit shaky
but it is important to remember that
most of the things I mentioned today are
absolutely fixable and that was the
reason of making this video because I
know know that AMD cares about criticism
and that they do want to fix things for
the end users at least the things that
can be fixed because as I said before
the absolute performance of this 7900 XT
is quite good and the only way to fix
their relative performance is to lower
the price because if it gets
significantly cheaper I'm sure that most
of us just would not care about the
confusing name business or the fact that
it is boosting a bit higher one day and
then a bit lower the next now the coil
wine is a bit more complicated to fix
especially for the cars that already
love the factory but for future batches
that too is definitely fixable and they
definitely should fix it because the GPU
Market is so competitive and even a
single downside like coil wine for
example can make a big difference at the
end of the day now that is all I have
for today I hope this video was
interesting enough let me know in the
comments down below what are your
thoughts about the XD and if you want to
make sure not to miss any of my future
videos try to click that subscribe
button bye guys and see in the next one
Prompt 0: Split text : Pass 2k characters in to the context and it will return a json with the required text
```
You are a personal assistant of mine. I need you to take youtube captions and split them into 1500-2000 character blocks. The blocks should all be self-enclosing. Meaning no context from the 1st block is needed in the 2nd block etc. for it to make sense. Omit any sponsorships and mentions of "this video was brought by".
VERY IMPORTANT: Respond with a JSON compatible array of strings.
```
Prompt 1: Extract main points
```
<system>
You are my personal assistant. I have a youtube video that needs to be broken down into the main points. Please take the captions I have given you and extract the main talking points along with quotes from the script. Send only a list of points with a quote attached to each point. Do not mention "The reviewer" or "You", only say full statements. Ignore any sponsorships or "this video was brought to you by" statements. Those should be excluded from the list of talking points. The talking points returned should look like this:
* Main talking point (Quote: "Quote from the captions")
</system>
<captions>
a part of the captions
</captions>
<response>
```
Prompt 2: Combine into blog mistral-7b-instruct-0.2
```
You are a copywriter. I just got my personal assistant to take the main talking points and quotes from a youtube video of mine. Take these main talking points and write an engaging blog article.
<talkingpoints>
all talking points from previous prompt
</talkingpoints>
<response>
```
Thoughs: The response was generally pretty OK. Got like 60% on quillbot AI detector.
Prompt 2 v2: Caption directly into blog
```
You are a copywriter. Your job is to write blog articles for all of my youtube videos to help with SEO and channel growth. I provide you with SRT captions from my youtube videos and you return an *engaging* blog article I can post to my website. You are allowed to use markdown for formatting. Make sure you do not mention any sponsors or "video was brough to you by" in the blog article.
```
Thoughts: Very robotic, didn't like it. Also tended to imagine stuff.
Prompt 2 v3: Main talking points
```
Take the main points and quotes from my YouTube video, picked out by my assistant, and turn them into a fun and informative blog post. The topic can change depending on the video. Keep it easy to read and relatable, like chatting with a friend. You can use markdown if it helps. The aim is to make content that feels natural and connects well with my audience, while still getting across the main ideas from the video.
```
full response. Model used: mistral-7b-instruct-v0.2
Title: My Honest Take on the AMD Radeon RX 7900 XT: A Powerful Card with Room for Improvement
Last week, AMD made waves in the tech world with the launch of their new graphics cards, specifically the RX 7900 XTX. As a tech enthusiast and content creator, I was excited to get my hands on the latest offering from AMD. However, due to unforeseen circumstances, I wasn't able to test the reference card. Instead, I've had the opportunity to review the Asus model of the RX 7900 XT.
Before we dive into the specifics of my review, I want to acknowledge AMD's success in creating high-end graphics cards and bringing some much-needed competition to the market. The graphics card landscape has been dominated by Nvidia for quite some time, and AMD's entry into the high-end market is a welcome change.
That being said, I also think that there are plenty of things that still need improvement with the RX 7900 XT. One of the most significant issues is the card's name and the confusion it causes for consumers. The XT model shares a similar name to the XTX, despite significant differences in specs. The XT has fewer cores, lower clock speeds, less memory, and a smaller memory bus than the XTX. AMD is pretty much doing the same thing Nvidia did with their controversial 4086 16 gig and 4080 12 gig cards. For 100 more, you can get a card that is 16 faster.
Another issue I've encountered is the inconsistent performance of the RX 7900 XT. In Call of Duty Modern Warfare, for instance, the card's performance was inconsistent, with one moment showing FPS numbers that align with other cards, and the next moment boosting much higher, outperforming the RTX 4090 and RTX 7900 XT. Every time we rebooted the PC, the XT acted differently for no clear reason. Seeing this inconsistent behavior always causes doubt whether everything is working well as it should.
Additionally, the ASUS TUF Gaming Radeon RX 7900 XT graphics card can produce coil whine, which is more noticeable due to its variable sound. This TUF Gaming from Asus doesn't always whine, but it does sit pretty frequently. And since the sound often changes depending on what's going on with your PC, it is way more noticeable than if it was just a consistent tone.
The oversized cooler on the TUF Gaming RX 7900 XT performs well when it's not whining, maintaining temperatures below 50 degrees with minimal noise. I managed to test the thermal and noise performance at one point where it wasn't whining, and this oversized Tough Cooler performed extremely well, barely going over 50 degrees with almost no noise at all.
However, there are a few other issues that need addressing. Power consumption is higher when using multiple monitors with the Radeon RX 7900 XT, leading to increased electricity costs. When connecting two monitors, the power consumption on the XT went up by a lot, 15 watts higher than the 4080.
The launch of the Radeon RX 7900 XT was definitely a bit shaky, but it is important to remember that most of the issues I've mentioned today are absolutely fixable. The only way to fix their relative performance is to lower the price. Coil whine is a bit more complicated to fix, especially for the cards that already left the factory. But for future batches, that too is definitely fixable.
In conclusion, the AMD Radeon RX 7900 XT is a powerful card with room for improvement. It offers a great high refresh rate experience at 1440p resolution and is capable of 4K gaming with FSR support. However, the inconsistent performance, coil whine, and power consumption issues are significant concerns that need to be addressed. AMD has the potential to create a truly competitive high-end graphics card, and I'm excited to see how they Title: My Honest Take on the AMD Radeon RX 7900 XT: A Powerful Card with Room for Improvement
Last week, AMD made waves in the tech world with the launch of their new graphics cards, specifically the RX 7900 XTX. As a tech enthusiast and content creator, I was excited to get my hands on the latest offering from AMD. However, due to unforeseen circumstances, I wasn't able to test the reference card. Instead, I've had the opportunity to review the Asus model of the RX 7900 XT.
Before we dive into the specifics of my review, I want to acknowledge AMD's success in creating high-end graphics cards and bringing some much-needed competition to the market. The graphics card landscape has been dominated by Nvidia for quite some time, and AMD's entry into the high-end market is a welcome change.
That being said, I also think that there are plenty of things that still need improvement with the RX 7900 XT. One of the most significant issues is the card's name and the confusion it causes for consumers. The XT model shares a similar name to the XTX, despite significant differences in specs. The XT has fewer cores, lower clock speeds, less memory, and a smaller memory bus than the XTX. AMD is pretty much doing the same thing Nvidia did with their controversial 4086 16 gig and 4080 12 gig cards. For 100 more, you can get a card that is 16 faster.
Another issue I've encountered is the inconsistent performance of the RX 7900 XT. In Call of Duty Modern Warfare, for instance, the card's performance was inconsistent, with one moment showing FPS numbers that align with other cards, and the next moment boosting much higher, outperforming the RTX 4090 and RTX 7900 XT. Every time we rebooted the PC, the XT acted differently for no clear reason. Seeing this inconsistent behavior always causes doubt whether everything is working well as it should.
Additionally, the ASUS TUF Gaming Radeon RX 7900 XT graphics card can produce coil whine, which is more noticeable due to its variable sound. This TUF Gaming from Asus doesn't always whine, but it does sit pretty frequently. And since the sound often changes depending on what's going on with your PC, it is way more noticeable than if it was just a consistent tone.
The oversized cooler on the TUF Gaming RX 7900 XT performs well when it's not whining, maintaining temperatures below 50 degrees with minimal noise. I managed to test the thermal and noise performance at one point where it wasn't whining, and this oversized Tough Cooler performed extremely well, barely going over 50 degrees with almost no noise at all.
However, there are a few other issues that need addressing. Power consumption is higher when using multiple monitors with the Radeon RX 7900 XT, leading to increased electricity costs. When connecting two monitors, the power consumption on the XT went up by a lot, 15 watts higher than the 4080.
The launch of the Radeon RX 7900 XT was definitely a bit shaky, but it is important to remember that most of the issues I've mentioned today are absolutely fixable. The only way to fix their relative performance is to lower the price. Coil whine is a bit more complicated to fix, especially for the cards that already left the factory. But for future batches, that too is definitely fixable.
In conclusion, the AMD Radeon RX 7900 XT is a powerful card with room for improvement. It offers a great high refresh rate experience at 1440p resolution and is capable of 4K gaming with FSR support. However, the inconsistent performance, coil whine, and power consumption issues are significant concerns that need to be addressed. AMD has the potential to create a truly competitive high-end graphics card, and I'm excited to see how they will address these issues moving forward.

File diff suppressed because one or more lines are too long

3478
yarn.lock

File diff suppressed because it is too large Load Diff