feat(core): initial implementation of syncing
This commit is contained in:
@@ -9,9 +9,10 @@ import { Config } from "../config/config"
|
||||
import { Flag } from "../flag/flag"
|
||||
import { Installation } from "../installation"
|
||||
|
||||
import { Database, NotFoundError, eq, and, or, gte, isNull, desc, like, inArray, lt } from "../storage/db"
|
||||
import { Database, NotFoundError, eq, and, gte, isNull, desc, like, inArray, lt } from "../storage/db"
|
||||
import { SyncEvent } from "../sync"
|
||||
import type { SQL } from "../storage/db"
|
||||
import { SessionTable, MessageTable, PartTable } from "./session.sql"
|
||||
import { SessionTable } from "./session.sql"
|
||||
import { ProjectTable } from "../project/project.sql"
|
||||
import { Storage } from "@/storage/storage"
|
||||
import { Log } from "../util/log"
|
||||
@@ -182,24 +183,44 @@ export namespace Session {
|
||||
export type GlobalInfo = z.output<typeof GlobalInfo>
|
||||
|
||||
export const Event = {
|
||||
Created: BusEvent.define(
|
||||
"session.created",
|
||||
z.object({
|
||||
Created: SyncEvent.define({
|
||||
type: "session.created",
|
||||
version: "v1",
|
||||
aggregate: "sessionID",
|
||||
schema: z.object({
|
||||
sessionID: z.string(),
|
||||
info: Info,
|
||||
}),
|
||||
),
|
||||
Updated: BusEvent.define(
|
||||
"session.updated",
|
||||
z.object({
|
||||
}),
|
||||
Shared: SyncEvent.define({
|
||||
type: "session.shared",
|
||||
version: "v1",
|
||||
aggregate: "sessionID",
|
||||
schema: z.object({
|
||||
sessionID: z.string(),
|
||||
url: z.string().optional(),
|
||||
}),
|
||||
}),
|
||||
Updated: SyncEvent.define({
|
||||
type: "session.updated",
|
||||
version: "v1",
|
||||
aggregate: "sessionID",
|
||||
schema: z.object({
|
||||
sessionID: z.string(),
|
||||
info: Info.partial().extend({
|
||||
time: Info.shape.time.partial().optional(),
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
Deleted: SyncEvent.define({
|
||||
type: "session.deleted",
|
||||
version: "v1",
|
||||
aggregate: "sessionID",
|
||||
schema: z.object({
|
||||
sessionID: z.string(),
|
||||
info: Info,
|
||||
}),
|
||||
),
|
||||
Deleted: BusEvent.define(
|
||||
"session.deleted",
|
||||
z.object({
|
||||
info: Info,
|
||||
}),
|
||||
),
|
||||
}),
|
||||
Diff: BusEvent.define(
|
||||
"session.diff",
|
||||
z.object({
|
||||
@@ -280,18 +301,8 @@ export namespace Session {
|
||||
)
|
||||
|
||||
export const touch = fn(SessionID.zod, async (sessionID) => {
|
||||
const now = Date.now()
|
||||
Database.use((db) => {
|
||||
const row = db
|
||||
.update(SessionTable)
|
||||
.set({ time_updated: now })
|
||||
.where(eq(SessionTable.id, sessionID))
|
||||
.returning()
|
||||
.get()
|
||||
if (!row) throw new NotFoundError({ message: `Session not found: ${sessionID}` })
|
||||
const info = fromRow(row)
|
||||
Database.effect(() => Bus.publish(Event.Updated, { info }))
|
||||
})
|
||||
const time = Date.now()
|
||||
SyncEvent.run(Event.Updated, { sessionID, info: { time: { updated: time } } })
|
||||
})
|
||||
|
||||
export async function createNext(input: {
|
||||
@@ -318,22 +329,18 @@ export namespace Session {
|
||||
},
|
||||
}
|
||||
log.info("created", result)
|
||||
Database.use((db) => {
|
||||
db.insert(SessionTable).values(toRow(result)).run()
|
||||
Database.effect(() =>
|
||||
Bus.publish(Event.Created, {
|
||||
info: result,
|
||||
}),
|
||||
)
|
||||
})
|
||||
|
||||
SyncEvent.run(Event.Created, { sessionID: result.id, info: result })
|
||||
|
||||
const cfg = await Config.get()
|
||||
if (!result.parentID && (Flag.OPENCODE_AUTO_SHARE || cfg.share === "auto"))
|
||||
share(result.id).catch(() => {
|
||||
// Silently ignore sharing errors during session creation
|
||||
})
|
||||
Bus.publish(Event.Updated, {
|
||||
info: result,
|
||||
})
|
||||
// Bus.publish(Event.Updated, {
|
||||
// id: result.id,
|
||||
// info: result,
|
||||
// })
|
||||
return result
|
||||
}
|
||||
|
||||
@@ -357,12 +364,9 @@ export namespace Session {
|
||||
}
|
||||
const { ShareNext } = await import("@/share/share-next")
|
||||
const share = await ShareNext.create(id)
|
||||
Database.use((db) => {
|
||||
const row = db.update(SessionTable).set({ share_url: share.url }).where(eq(SessionTable.id, id)).returning().get()
|
||||
if (!row) throw new NotFoundError({ message: `Session not found: ${id}` })
|
||||
const info = fromRow(row)
|
||||
Database.effect(() => Bus.publish(Event.Updated, { info }))
|
||||
})
|
||||
|
||||
SyncEvent.run(Event.Shared, { sessionID: id, url: share.url })
|
||||
|
||||
return share
|
||||
})
|
||||
|
||||
@@ -370,12 +374,8 @@ export namespace Session {
|
||||
// Use ShareNext to remove the share (same as share function uses ShareNext to create)
|
||||
const { ShareNext } = await import("@/share/share-next")
|
||||
await ShareNext.remove(id)
|
||||
Database.use((db) => {
|
||||
const row = db.update(SessionTable).set({ share_url: null }).where(eq(SessionTable.id, id)).returning().get()
|
||||
if (!row) throw new NotFoundError({ message: `Session not found: ${id}` })
|
||||
const info = fromRow(row)
|
||||
Database.effect(() => Bus.publish(Event.Updated, { info }))
|
||||
})
|
||||
|
||||
SyncEvent.run(Event.Shared, { sessionID: id, url: undefined })
|
||||
})
|
||||
|
||||
export const setTitle = fn(
|
||||
@@ -384,18 +384,7 @@ export namespace Session {
|
||||
title: z.string(),
|
||||
}),
|
||||
async (input) => {
|
||||
return Database.use((db) => {
|
||||
const row = db
|
||||
.update(SessionTable)
|
||||
.set({ title: input.title })
|
||||
.where(eq(SessionTable.id, input.sessionID))
|
||||
.returning()
|
||||
.get()
|
||||
if (!row) throw new NotFoundError({ message: `Session not found: ${input.sessionID}` })
|
||||
const info = fromRow(row)
|
||||
Database.effect(() => Bus.publish(Event.Updated, { info }))
|
||||
return info
|
||||
})
|
||||
SyncEvent.run(Event.Updated, { sessionID: input.sessionID, info: { title: input.title } })
|
||||
},
|
||||
)
|
||||
|
||||
@@ -405,18 +394,7 @@ export namespace Session {
|
||||
time: z.number().optional(),
|
||||
}),
|
||||
async (input) => {
|
||||
return Database.use((db) => {
|
||||
const row = db
|
||||
.update(SessionTable)
|
||||
.set({ time_archived: input.time })
|
||||
.where(eq(SessionTable.id, input.sessionID))
|
||||
.returning()
|
||||
.get()
|
||||
if (!row) throw new NotFoundError({ message: `Session not found: ${input.sessionID}` })
|
||||
const info = fromRow(row)
|
||||
Database.effect(() => Bus.publish(Event.Updated, { info }))
|
||||
return info
|
||||
})
|
||||
SyncEvent.run(Event.Updated, { sessionID: input.sessionID, info: { time: { archived: input.time } } })
|
||||
},
|
||||
)
|
||||
|
||||
@@ -426,17 +404,9 @@ export namespace Session {
|
||||
permission: Permission.Ruleset,
|
||||
}),
|
||||
async (input) => {
|
||||
return Database.use((db) => {
|
||||
const row = db
|
||||
.update(SessionTable)
|
||||
.set({ permission: input.permission, time_updated: Date.now() })
|
||||
.where(eq(SessionTable.id, input.sessionID))
|
||||
.returning()
|
||||
.get()
|
||||
if (!row) throw new NotFoundError({ message: `Session not found: ${input.sessionID}` })
|
||||
const info = fromRow(row)
|
||||
Database.effect(() => Bus.publish(Event.Updated, { info }))
|
||||
return info
|
||||
SyncEvent.run(Event.Updated, {
|
||||
sessionID: input.sessionID,
|
||||
info: { permission: input.permission, time: { updated: Date.now() } },
|
||||
})
|
||||
},
|
||||
)
|
||||
@@ -448,42 +418,24 @@ export namespace Session {
|
||||
summary: Info.shape.summary,
|
||||
}),
|
||||
async (input) => {
|
||||
return Database.use((db) => {
|
||||
const row = db
|
||||
.update(SessionTable)
|
||||
.set({
|
||||
revert: input.revert ?? null,
|
||||
summary_additions: input.summary?.additions,
|
||||
summary_deletions: input.summary?.deletions,
|
||||
summary_files: input.summary?.files,
|
||||
time_updated: Date.now(),
|
||||
})
|
||||
.where(eq(SessionTable.id, input.sessionID))
|
||||
.returning()
|
||||
.get()
|
||||
if (!row) throw new NotFoundError({ message: `Session not found: ${input.sessionID}` })
|
||||
const info = fromRow(row)
|
||||
Database.effect(() => Bus.publish(Event.Updated, { info }))
|
||||
return info
|
||||
SyncEvent.run(Event.Updated, {
|
||||
sessionID: input.sessionID,
|
||||
info: {
|
||||
summary: input.summary,
|
||||
time: { updated: Date.now() },
|
||||
revert: input.revert,
|
||||
},
|
||||
})
|
||||
},
|
||||
)
|
||||
|
||||
export const clearRevert = fn(SessionID.zod, async (sessionID) => {
|
||||
return Database.use((db) => {
|
||||
const row = db
|
||||
.update(SessionTable)
|
||||
.set({
|
||||
revert: null,
|
||||
time_updated: Date.now(),
|
||||
})
|
||||
.where(eq(SessionTable.id, sessionID))
|
||||
.returning()
|
||||
.get()
|
||||
if (!row) throw new NotFoundError({ message: `Session not found: ${sessionID}` })
|
||||
const info = fromRow(row)
|
||||
Database.effect(() => Bus.publish(Event.Updated, { info }))
|
||||
return info
|
||||
SyncEvent.run(Event.Updated, {
|
||||
sessionID,
|
||||
info: {
|
||||
time: { updated: Date.now() },
|
||||
revert: undefined,
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
@@ -493,22 +445,12 @@ export namespace Session {
|
||||
summary: Info.shape.summary,
|
||||
}),
|
||||
async (input) => {
|
||||
return Database.use((db) => {
|
||||
const row = db
|
||||
.update(SessionTable)
|
||||
.set({
|
||||
summary_additions: input.summary?.additions,
|
||||
summary_deletions: input.summary?.deletions,
|
||||
summary_files: input.summary?.files,
|
||||
time_updated: Date.now(),
|
||||
})
|
||||
.where(eq(SessionTable.id, input.sessionID))
|
||||
.returning()
|
||||
.get()
|
||||
if (!row) throw new NotFoundError({ message: `Session not found: ${input.sessionID}` })
|
||||
const info = fromRow(row)
|
||||
Database.effect(() => Bus.publish(Event.Updated, { info }))
|
||||
return info
|
||||
SyncEvent.run(Event.Updated, {
|
||||
sessionID: input.sessionID,
|
||||
info: {
|
||||
time: { updated: Date.now() },
|
||||
summary: input.summary,
|
||||
},
|
||||
})
|
||||
},
|
||||
)
|
||||
@@ -662,46 +604,25 @@ export namespace Session {
|
||||
})
|
||||
|
||||
export const remove = fn(SessionID.zod, async (sessionID) => {
|
||||
const project = Instance.project
|
||||
try {
|
||||
const session = await get(sessionID)
|
||||
for (const child of await children(sessionID)) {
|
||||
await remove(child.id)
|
||||
}
|
||||
await unshare(sessionID).catch(() => {})
|
||||
// CASCADE delete handles messages and parts automatically
|
||||
Database.use((db) => {
|
||||
db.delete(SessionTable).where(eq(SessionTable.id, sessionID)).run()
|
||||
Database.effect(() =>
|
||||
Bus.publish(Event.Deleted, {
|
||||
info: session,
|
||||
}),
|
||||
)
|
||||
})
|
||||
|
||||
SyncEvent.run(Event.Deleted, { sessionID, info: session })
|
||||
} catch (e) {
|
||||
log.error(e)
|
||||
}
|
||||
})
|
||||
|
||||
export const updateMessage = fn(MessageV2.Info, async (msg) => {
|
||||
const time_created = msg.time.created
|
||||
const { id, sessionID, ...data } = msg
|
||||
Database.use((db) => {
|
||||
db.insert(MessageTable)
|
||||
.values({
|
||||
id,
|
||||
session_id: sessionID,
|
||||
time_created,
|
||||
data,
|
||||
})
|
||||
.onConflictDoUpdate({ target: MessageTable.id, set: { data } })
|
||||
.run()
|
||||
Database.effect(() =>
|
||||
Bus.publish(MessageV2.Event.Updated, {
|
||||
info: msg,
|
||||
}),
|
||||
)
|
||||
SyncEvent.run(MessageV2.Event.Updated, {
|
||||
sessionID: msg.sessionID,
|
||||
info: msg,
|
||||
})
|
||||
|
||||
return msg
|
||||
})
|
||||
|
||||
@@ -711,17 +632,9 @@ export namespace Session {
|
||||
messageID: MessageID.zod,
|
||||
}),
|
||||
async (input) => {
|
||||
// CASCADE delete handles parts automatically
|
||||
Database.use((db) => {
|
||||
db.delete(MessageTable)
|
||||
.where(and(eq(MessageTable.id, input.messageID), eq(MessageTable.session_id, input.sessionID)))
|
||||
.run()
|
||||
Database.effect(() =>
|
||||
Bus.publish(MessageV2.Event.Removed, {
|
||||
sessionID: input.sessionID,
|
||||
messageID: input.messageID,
|
||||
}),
|
||||
)
|
||||
SyncEvent.run(MessageV2.Event.Removed, {
|
||||
sessionID: input.sessionID,
|
||||
messageID: input.messageID,
|
||||
})
|
||||
return input.messageID
|
||||
},
|
||||
@@ -734,17 +647,10 @@ export namespace Session {
|
||||
partID: PartID.zod,
|
||||
}),
|
||||
async (input) => {
|
||||
Database.use((db) => {
|
||||
db.delete(PartTable)
|
||||
.where(and(eq(PartTable.id, input.partID), eq(PartTable.session_id, input.sessionID)))
|
||||
.run()
|
||||
Database.effect(() =>
|
||||
Bus.publish(MessageV2.Event.PartRemoved, {
|
||||
sessionID: input.sessionID,
|
||||
messageID: input.messageID,
|
||||
partID: input.partID,
|
||||
}),
|
||||
)
|
||||
SyncEvent.run(MessageV2.Event.PartRemoved, {
|
||||
sessionID: input.sessionID,
|
||||
messageID: input.messageID,
|
||||
partID: input.partID,
|
||||
})
|
||||
return input.partID
|
||||
},
|
||||
@@ -753,24 +659,10 @@ export namespace Session {
|
||||
const UpdatePartInput = MessageV2.Part
|
||||
|
||||
export const updatePart = fn(UpdatePartInput, async (part) => {
|
||||
const { id, messageID, sessionID, ...data } = part
|
||||
const time = Date.now()
|
||||
Database.use((db) => {
|
||||
db.insert(PartTable)
|
||||
.values({
|
||||
id,
|
||||
message_id: messageID,
|
||||
session_id: sessionID,
|
||||
time_created: time,
|
||||
data,
|
||||
})
|
||||
.onConflictDoUpdate({ target: PartTable.id, set: { data } })
|
||||
.run()
|
||||
Database.effect(() =>
|
||||
Bus.publish(MessageV2.Event.PartUpdated, {
|
||||
part: structuredClone(part),
|
||||
}),
|
||||
)
|
||||
SyncEvent.run(MessageV2.Event.PartUpdated, {
|
||||
sessionID: part.sessionID,
|
||||
part: structuredClone(part),
|
||||
time: Date.now(),
|
||||
})
|
||||
return part
|
||||
})
|
||||
|
||||
@@ -6,8 +6,9 @@ import { APICallError, convertToModelMessages, LoadAPIKeyError, type ModelMessag
|
||||
import { LSP } from "../lsp"
|
||||
import { Snapshot } from "@/snapshot"
|
||||
import { fn } from "@/util/fn"
|
||||
import { Database, NotFoundError, and, desc, eq, inArray, lt, or } from "@/storage/db"
|
||||
import { MessageTable, PartTable, SessionTable } from "./session.sql"
|
||||
import { Database, eq, desc, inArray } from "@/storage/db"
|
||||
import { SyncEvent } from "../sync"
|
||||
import { MessageTable, PartTable } from "./session.sql"
|
||||
import { ProviderTransform } from "@/provider/transform"
|
||||
import { STATUS_CODES } from "http"
|
||||
import { Storage } from "@/storage/storage"
|
||||
@@ -449,25 +450,34 @@ export namespace MessageV2 {
|
||||
export type Info = z.infer<typeof Info>
|
||||
|
||||
export const Event = {
|
||||
Updated: BusEvent.define(
|
||||
"message.updated",
|
||||
z.object({
|
||||
Updated: SyncEvent.define({
|
||||
type: "message.updated",
|
||||
version: "v1",
|
||||
aggregate: "sessionID",
|
||||
schema: z.object({
|
||||
sessionID: z.string(),
|
||||
info: Info,
|
||||
}),
|
||||
),
|
||||
Removed: BusEvent.define(
|
||||
"message.removed",
|
||||
z.object({
|
||||
}),
|
||||
Removed: SyncEvent.define({
|
||||
type: "message.removed",
|
||||
version: "v1",
|
||||
aggregate: "sessionID",
|
||||
schema: z.object({
|
||||
sessionID: SessionID.zod,
|
||||
messageID: MessageID.zod,
|
||||
}),
|
||||
),
|
||||
PartUpdated: BusEvent.define(
|
||||
"message.part.updated",
|
||||
z.object({
|
||||
}),
|
||||
PartUpdated: SyncEvent.define({
|
||||
type: "message.part.updated",
|
||||
version: "v1",
|
||||
aggregate: "sessionID",
|
||||
schema: z.object({
|
||||
sessionID: z.string(),
|
||||
part: Part,
|
||||
time: z.number(),
|
||||
}),
|
||||
),
|
||||
}),
|
||||
PartDelta: BusEvent.define(
|
||||
"message.part.delta",
|
||||
z.object({
|
||||
@@ -478,14 +488,16 @@ export namespace MessageV2 {
|
||||
delta: z.string(),
|
||||
}),
|
||||
),
|
||||
PartRemoved: BusEvent.define(
|
||||
"message.part.removed",
|
||||
z.object({
|
||||
PartRemoved: SyncEvent.define({
|
||||
type: "message.part.removed",
|
||||
version: "v1",
|
||||
aggregate: "sessionID",
|
||||
schema: z.object({
|
||||
sessionID: SessionID.zod,
|
||||
messageID: MessageID.zod,
|
||||
partID: PartID.zod,
|
||||
}),
|
||||
),
|
||||
}),
|
||||
}
|
||||
|
||||
export const WithParts = z.object({
|
||||
|
||||
@@ -0,0 +1,135 @@
|
||||
import { NotFoundError, eq, and } from "../storage/db"
|
||||
import { SyncEvent } from "@/sync"
|
||||
import { Session } from "./index"
|
||||
import { MessageV2 } from "./message-v2"
|
||||
import { SessionTable, MessageTable, PartTable } from "./session.sql"
|
||||
import { ProjectTable } from "../project/project.sql"
|
||||
|
||||
export type DeepPartial<T> = {
|
||||
[K in keyof T]?: T[K] extends object ? DeepPartial<T[K]> : T[K]
|
||||
}
|
||||
|
||||
function grab<T extends object, K1 extends keyof T, X>(
|
||||
obj: T,
|
||||
field1: K1,
|
||||
cb?: (val: NonNullable<T[K1]>) => X,
|
||||
): X | undefined {
|
||||
if (obj == undefined || !(field1 in obj)) return undefined
|
||||
|
||||
const val = obj[field1]
|
||||
if (val && typeof val === "object" && cb) {
|
||||
return cb(val)
|
||||
}
|
||||
return (val === undefined ? null : val) as X | undefined
|
||||
}
|
||||
|
||||
export function toPartialRow(info: DeepPartial<Session.Info>) {
|
||||
const obj = {
|
||||
id: grab(info, "id"),
|
||||
project_id: grab(info, "projectID"),
|
||||
workspace_id: grab(info, "workspaceID"),
|
||||
parent_id: grab(info, "parentID"),
|
||||
slug: grab(info, "slug"),
|
||||
directory: grab(info, "directory"),
|
||||
title: grab(info, "title"),
|
||||
version: grab(info, "version"),
|
||||
share_url: grab(info, "share", (v) => grab(v, "url")),
|
||||
summary_additions: grab(info, "summary", (v) => grab(v, "additions")),
|
||||
summary_deletions: grab(info, "summary", (v) => grab(v, "deletions")),
|
||||
summary_files: grab(info, "summary", (v) => grab(v, "files")),
|
||||
summary_diffs: grab(info, "summary", (v) => grab(v, "diffs")),
|
||||
revert: grab(info, "revert"),
|
||||
permission: grab(info, "permission"),
|
||||
time_created: grab(info, "time", (v) => grab(v, "created")),
|
||||
time_updated: grab(info, "time", (v) => grab(v, "updated")),
|
||||
time_compacting: grab(info, "time", (v) => grab(v, "compacting")),
|
||||
time_archived: grab(info, "time", (v) => grab(v, "archived")),
|
||||
}
|
||||
|
||||
return Object.fromEntries(Object.entries(obj).filter(([_, val]) => val !== undefined))
|
||||
}
|
||||
|
||||
export default [
|
||||
SyncEvent.project(Session.Event.Created, (db, data) => {
|
||||
const existing = db
|
||||
.select({ id: ProjectTable.id })
|
||||
.from(ProjectTable)
|
||||
.where(eq(ProjectTable.id, data.info.projectID))
|
||||
.get()
|
||||
if (!existing) {
|
||||
// Create a (temporary) project to make this work. In the future
|
||||
// we should separate sessions and projects
|
||||
db.insert(ProjectTable)
|
||||
.values({
|
||||
id: data.info.projectID,
|
||||
worktree: data.info.directory,
|
||||
sandboxes: [],
|
||||
})
|
||||
.run()
|
||||
}
|
||||
|
||||
db.insert(SessionTable).values(Session.toRow(data.info)).run()
|
||||
}),
|
||||
|
||||
SyncEvent.project(Session.Event.Updated, (db, data) => {
|
||||
const info = data.info
|
||||
const row = db.update(SessionTable).set(toPartialRow(info)).where(eq(SessionTable.id, data.sessionID)).returning().get()
|
||||
if (!row) throw new NotFoundError({ message: `Session not found: ${data.sessionID}` })
|
||||
}),
|
||||
|
||||
SyncEvent.project(Session.Event.Shared, (db, data) => {
|
||||
const row = db
|
||||
.update(SessionTable)
|
||||
.set({ share_url: data.url })
|
||||
.where(eq(SessionTable.id, data.sessionID))
|
||||
.returning()
|
||||
.get()
|
||||
if (!row) throw new NotFoundError({ message: `Session not found: ${data.sessionID}` })
|
||||
}),
|
||||
|
||||
SyncEvent.project(Session.Event.Deleted, (db, data) => {
|
||||
db.delete(SessionTable).where(eq(SessionTable.id, data.sessionID)).run()
|
||||
}),
|
||||
|
||||
SyncEvent.project(MessageV2.Event.Updated, (db, data) => {
|
||||
const time_created = data.info.time.created
|
||||
const { id, sessionID, ...rest } = data.info
|
||||
|
||||
db.insert(MessageTable)
|
||||
.values({
|
||||
id,
|
||||
session_id: sessionID,
|
||||
time_created,
|
||||
data: rest,
|
||||
})
|
||||
.onConflictDoUpdate({ target: MessageTable.id, set: { data: rest } })
|
||||
.run()
|
||||
}),
|
||||
|
||||
SyncEvent.project(MessageV2.Event.Removed, (db, data) => {
|
||||
db.delete(MessageTable)
|
||||
.where(and(eq(MessageTable.id, data.messageID), eq(MessageTable.session_id, data.sessionID)))
|
||||
.run()
|
||||
}),
|
||||
|
||||
SyncEvent.project(MessageV2.Event.PartRemoved, (db, data) => {
|
||||
db.delete(PartTable)
|
||||
.where(and(eq(PartTable.id, data.partID), eq(PartTable.session_id, data.sessionID)))
|
||||
.run()
|
||||
}),
|
||||
|
||||
SyncEvent.project(MessageV2.Event.PartUpdated, (db, data) => {
|
||||
const { id, messageID, sessionID, ...rest } = data.part
|
||||
|
||||
db.insert(PartTable)
|
||||
.values({
|
||||
id,
|
||||
message_id: messageID,
|
||||
session_id: sessionID,
|
||||
time_created: data.time,
|
||||
data: rest,
|
||||
})
|
||||
.onConflictDoUpdate({ target: PartTable.id, set: { data: rest } })
|
||||
.run()
|
||||
}),
|
||||
]
|
||||
@@ -4,8 +4,7 @@ import { Snapshot } from "../snapshot"
|
||||
import { MessageV2 } from "./message-v2"
|
||||
import { Session } from "."
|
||||
import { Log } from "../util/log"
|
||||
import { Database, eq } from "../storage/db"
|
||||
import { MessageTable, PartTable } from "./session.sql"
|
||||
import { SyncEvent } from "../sync"
|
||||
import { Storage } from "@/storage/storage"
|
||||
import { Bus } from "../bus"
|
||||
import { SessionPrompt } from "./prompt"
|
||||
@@ -113,8 +112,10 @@ export namespace SessionRevert {
|
||||
remove.push(msg)
|
||||
}
|
||||
for (const msg of remove) {
|
||||
Database.use((db) => db.delete(MessageTable).where(eq(MessageTable.id, msg.info.id)).run())
|
||||
await Bus.publish(MessageV2.Event.Removed, { sessionID: sessionID, messageID: msg.info.id })
|
||||
SyncEvent.run(MessageV2.Event.Removed, {
|
||||
sessionID: sessionID,
|
||||
messageID: msg.info.id,
|
||||
})
|
||||
}
|
||||
if (session.revert.partID && target) {
|
||||
const partID = session.revert.partID
|
||||
@@ -124,8 +125,7 @@ export namespace SessionRevert {
|
||||
const removeParts = target.parts.slice(removeStart)
|
||||
target.parts = preserveParts
|
||||
for (const part of removeParts) {
|
||||
Database.use((db) => db.delete(PartTable).where(eq(PartTable.id, part.id)).run())
|
||||
await Bus.publish(MessageV2.Event.PartRemoved, {
|
||||
SyncEvent.run(MessageV2.Event.PartRemoved, {
|
||||
sessionID: sessionID,
|
||||
messageID: target.info.id,
|
||||
partID: part.id,
|
||||
|
||||
Reference in New Issue
Block a user