Files
hub-monorepo/packages/hub-nodejs/examples/replicate-data-postgres/db.ts

273 lines
6.8 KiB
TypeScript

import {
Kysely,
CamelCasePlugin,
Generated,
GeneratedAlways,
Migrator,
FileMigrationProvider,
NO_MIGRATIONS,
} from "kysely";
import { PostgresJSDialect } from "kysely-postgres-js";
import postgres from "postgres";
import { MessageType, ReactionType, UserDataType, HashScheme, SignatureScheme } from "@farcaster/hub-nodejs";
import * as path from "path";
import { promises as fs } from "fs";
import { fileURLToPath } from "url";
import { Logger } from "./log";
import { err, ok, Result } from "neverthrow";
export interface Database {
hubSubscriptions: {
host: string;
lastEventId: number;
};
casts: {
id: GeneratedAlways<string>;
createdAt: Generated<Date>;
updatedAt: Generated<Date>;
deletedAt: Date | null;
timestamp: Date;
fid: number;
text: string;
hash: Uint8Array;
parentHash: Uint8Array | null;
parentFid: number | null;
parentUrl: string | null;
embeds: Generated<
{
url?: string | undefined;
castId?: object | undefined;
}[]
>;
mentions: Generated<number[]>;
mentionsPositions: Generated<number[]>;
};
messages: {
id: GeneratedAlways<string>;
createdAt: Generated<Date>;
updatedAt: Generated<Date>;
deletedAt: Date | null;
revokedAt: Date | null;
prunedAt: Date | null;
fid: number;
messageType: MessageType;
timestamp: Date;
hash: Uint8Array;
hashScheme: HashScheme;
signature: Uint8Array;
signatureScheme: SignatureScheme;
signer: Uint8Array;
raw: Uint8Array;
};
reactions: {
id: GeneratedAlways<string>;
createdAt: Generated<Date>;
updatedAt: Generated<Date>;
deletedAt: Date | null;
fid: number;
reactionType: ReactionType;
timestamp: Date;
hash: Uint8Array;
targetHash: Uint8Array | null;
targetFid: number | null;
targetUrl: string | null;
};
signers: {
id: GeneratedAlways<string>;
createdAt: Generated<Date>;
updatedAt: Generated<Date>;
deletedAt: Date | null;
timestamp: Date;
fid: number;
custodyAddress: Uint8Array | null;
signer: Uint8Array;
name: string | null;
hash: Uint8Array | null;
};
storage: {
id: GeneratedAlways<string>;
createdAt: Generated<Date>;
updatedAt: Generated<Date>;
timestamp: Date;
fid: number;
units: number;
expiry: Date;
};
verifications: {
id: GeneratedAlways<string>;
createdAt: Generated<Date>;
updatedAt: Generated<Date>;
deletedAt: Date | null;
fid: number;
timestamp: Date;
hash: Uint8Array;
claim: {
address: string;
ethSignature: string;
blockHash: string;
};
};
userData: {
id: GeneratedAlways<string>;
createdAt: Generated<Date>;
updatedAt: Generated<Date>;
deletedAt: Date | null;
timestamp: Date;
fid: number;
hash: Uint8Array;
type: UserDataType;
value: string;
};
fids: {
fid: number;
createdAt: Generated<Date>;
updatedAt: Generated<Date>;
custodyAddress: Uint8Array;
};
fnames: {
fname: string;
fid: number;
createdAt: Generated<Date>;
updatedAt: Generated<Date>;
custodyAddress: Uint8Array | null;
deletedAt: Date | null;
};
links: {
hash: Uint8Array;
id: GeneratedAlways<string>;
fid: number;
targetFid: number | null;
type: string;
timestamp: Date;
createdAt: Generated<Date>;
updatedAt: Generated<Date>;
displayTimestamp: Date | null;
deletedAt: Date | null;
};
}
export const getDbClient = (connectionString: string) => {
return new Kysely<Database>({
dialect: new PostgresJSDialect({
connectionString,
options: {
max: 10,
types: {
// BigInts will not exceed Number.MAX_SAFE_INTEGER for our use case.
// Return as JavaScript's `number` type so it's easier to work with.
bigint: {
to: 20,
from: 20,
// rome-ignore lint/suspicious/noExplicitAny: legacy code, avoid using ignore for new code
parse: (x: any) => Number(x),
// rome-ignore lint/suspicious/noExplicitAny: legacy code, avoid using ignore for new code
serialize: (x: any) => x.toString(),
},
},
},
postgres,
}),
plugins: [new CamelCasePlugin()],
});
};
// rome-ignore lint/suspicious/noExplicitAny: legacy code, avoid using ignore for new code
export const migrateToLatest = async (db: Kysely<any>, log: Logger): Promise<Result<void, unknown>> => {
const migrator = new Migrator({
db,
provider: new FileMigrationProvider({
fs,
path,
migrationFolder: path.join(path.dirname(fileURLToPath(import.meta.url)), "migrations"),
}),
});
const { error, results } = await migrator.migrateToLatest();
results?.forEach((it) => {
if (it.status === "Success") {
log.info(`migration "${it.migrationName}" was executed successfully`);
} else if (it.status === "Error") {
log.error(`failed to execute migration "${it.migrationName}"`);
}
});
if (error) {
log.error("failed to migrate");
log.error(error);
return err(error);
}
return ok(undefined);
};
// rome-ignore lint/suspicious/noExplicitAny: legacy code, avoid using ignore for new code
export const migrateResetEntirely = async (db: Kysely<any>, log: Logger): Promise<Result<void, unknown>> => {
const migrator = new Migrator({
db,
provider: new FileMigrationProvider({
fs,
path,
migrationFolder: path.join(path.dirname(fileURLToPath(import.meta.url)), "migrations"),
}),
});
const { error, results } = await migrator.migrateTo(NO_MIGRATIONS);
results?.forEach((it) => {
if (it.status === "Success") {
log.info(`migration down "${it.migrationName}" was executed successfully`);
} else if (it.status === "Error") {
log.error(`failed to execute migration down "${it.migrationName}"`);
}
});
if (error) {
log.error("failed to migrate");
log.error(error);
return err(error);
}
return ok(undefined);
};
// rome-ignore lint/suspicious/noExplicitAny: legacy code, avoid using ignore for new code
export const migrateOneUp = async (db: Kysely<any>, log: Logger): Promise<Result<void, unknown>> => {
const migrator = new Migrator({
db,
provider: new FileMigrationProvider({
fs,
path,
migrationFolder: path.join(path.dirname(fileURLToPath(import.meta.url)), "migrations"),
}),
});
const { error, results } = await migrator.migrateUp();
results?.forEach((it) => {
if (it.status === "Success") {
log.info(`migration "${it.migrationName}" was executed successfully`);
} else if (it.status === "Error") {
log.error(`failed to execute migration "${it.migrationName}"`);
}
});
if (error) {
log.error("failed to migrate");
log.error(error);
return err(error);
}
return ok(undefined);
};