auron
Version:
Interact with your ATProto labeler from your terminal
170 lines (169 loc) • 7.29 kB
JavaScript
;
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.database = void 0;
const kysely_1 = require("kysely");
const better_sqlite3_1 = __importDefault(require("better-sqlite3"));
const initializers_1 = require("../schemas/initializers");
const transformers_1 = require("./subject/transformers");
// Initialize Kysely with SQLite
const db = new kysely_1.Kysely({
dialect: new kysely_1.SqliteDialect({
database: new better_sqlite3_1.default(process.env.LOCAL_DB_PATH || "aurondb.sqlite"),
}),
});
// Create the subjects table if it doesn't exist
exports.database = {
async initialize() {
await (0, initializers_1.initializeSubjectTable)(db);
await (0, initializers_1.initializeEventTable)(db);
await (0, initializers_1.initializeRepoTable)(db);
await (0, initializers_1.initializeRecordTable)(db);
await (0, initializers_1.initializeProfileTable)(db);
},
async insertSubjects(subjectStatuses) {
await this.initialize();
const ids = subjectStatuses.map(({ id }) => id);
// This is questionable, probably could do update on conflict or something but quick n dirty for now
await db.deleteFrom("subjects").where("id", "in", ids).execute();
const { ref } = db.dynamic;
return await db
.insertInto("subjects")
.values(subjectStatuses)
.onConflict((oc) => {
return oc.doUpdateSet({
reviewState: (0, kysely_1.sql) `${ref(`excluded.reviewState`)}`,
lastReviewedAt: (0, kysely_1.sql) `${ref(`excluded.lastReviewedAt`)}`,
lastReportedAt: (0, kysely_1.sql) `${ref(`excluded.lastReportedAt`)}`,
updatedAt: (0, kysely_1.sql) `${ref("excluded.updatedAt")}`,
lastReviewedBy: (0, kysely_1.sql) `${ref("excluded.lastReviewedBy")}`,
takendown: (0, kysely_1.sql) `${ref("excluded.takendown")}`,
tags: (0, kysely_1.sql) `${ref("excluded.tags")}`,
lastAppealedAt: (0, kysely_1.sql) `${ref("excluded.lastAppealedAt")}`,
suspendUntil: (0, kysely_1.sql) `${ref("excluded.suspendUntil")}`,
muteUntil: (0, kysely_1.sql) `${ref("excluded.muteUntil")}`,
comment: (0, kysely_1.sql) `${ref("excluded.comment")}`,
});
})
.execute();
},
async insertEvents(events) {
await this.initialize();
const ids = events.map(({ id }) => id);
// This is questionable, probably could do update on conflict or something but quick n dirty for now
await db.deleteFrom("events").where("id", "in", ids).execute();
return await db.insertInto("events").values(events).execute();
},
async listSubjects({ subjectType, takendown, cursor, limit, }) {
await this.initialize();
let builder = db
.selectFrom("subjects")
.leftJoin("repos", "subjects.did", "repos.did")
.leftJoin("profiles", "subjects.did", "profiles.did")
.leftJoin("records", (join) => join.onRef("records.uri", "=", (0, kysely_1.sql) `'at://' || subjects.did || '/' || subjects.recordPath`))
.selectAll(["subjects", "repos", "records", "profiles"]);
if (subjectType === "account") {
builder = builder.where("recordPath", "=", "");
}
else if (subjectType === "record") {
builder = builder.where("recordPath", "!=", "");
}
if (takendown !== undefined) {
builder = builder.where("takendown", "=", takendown ? 1 : 0);
}
if (limit) {
builder = builder.limit(limit);
}
if (cursor) {
builder = builder.where("lastReportedAt", "<", cursor);
}
const results = await builder.orderBy("lastReportedAt", "desc").execute();
// @ts-ignore
return results.map(transformers_1.transformFullStatusFromDatabase);
},
async clearSubjects() {
await this.initialize();
await db.deleteFrom("subjects").execute();
},
async getMissingRepoDids() {
await this.initialize();
const results = await db
.selectFrom("subjects")
.select("did")
.distinct()
.where("did", "not in", (qb) => qb.selectFrom("repos").select(["did"]))
.execute();
return results.map(({ did }) => did);
},
async getMissingProfileDids() {
await this.initialize();
const syncPeriod = new Date(Date.now() - 1000 * 60 * 60 * 24).toISOString();
const results = await db
.selectFrom("subjects")
.select("did")
.distinct()
.where("did", "not in", (qb) => {
return qb
.selectFrom("profiles")
.select("did")
.where("syncedAt", ">", syncPeriod);
})
.execute();
return results.map(({ did }) => did);
},
async getMissingRecordUris() {
await this.initialize();
const results = await db
.selectFrom("subjects")
.leftJoin("records", (join) => join.onRef("records.uri", "=", (0, kysely_1.sql) `'at://' || subjects.did || '/' || subjects.recordPath`))
.select(["subjects.did", "subjects.recordPath"])
.where("records.uri", "is", null)
.where("subjects.recordPath", "!=", "")
.execute();
return results.map(({ did, recordPath }) => `at://${did}/${recordPath}`);
},
async saveRepos(repos) {
await this.initialize();
await db.insertInto("repos").values(repos).execute();
},
async saveRecords(records) {
await this.initialize();
await db.insertInto("records").values(records).execute();
},
async saveProfiles(profiles) {
await this.initialize();
const dids = profiles.map(({ did }) => did);
// Remove all existing profiles first
if (dids.length) {
await db.deleteFrom("profiles").where("did", "in", dids).execute();
}
await db.insertInto("profiles").values(profiles).execute();
},
async getEvents(opts) {
let qb = db.selectFrom("events");
if (opts.cursor) {
qb = qb.where("id", opts.order === "asc" ? ">" : "<", opts.cursor);
}
if (opts.types.length) {
qb = qb.where("action", "in", opts.types);
}
if (opts.createdAfter) {
qb = qb.where("createdAt", ">", opts.createdAfter);
}
if (opts.createdBefore) {
qb = qb.where("createdAt", "<", opts.createdBefore);
}
return qb
.orderBy("id", opts.order || "asc")
.select(["subjectDid", "subjectUri", "action", "createdAt", "createdBy"])
.execute();
},
async clear() {
await db.schema.dropTable("events").execute();
await db.schema.dropTable("subjects").execute();
await db.schema.dropTable("repos").execute();
await db.schema.dropTable("profiles").execute();
},
};