feat: backfill entities with new organization id
This commit is contained in:
parent
4425f22f3a
commit
0fafe4114b
9 changed files with 3317 additions and 9 deletions
5
app/drizzle/0038_shallow_pride.sql
Normal file
5
app/drizzle/0038_shallow_pride.sql
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
DROP INDEX `volumes_table_name_unique`;--> statement-breakpoint
|
||||
ALTER TABLE `volumes_table` ADD `organization_id` text REFERENCES organization(id);--> statement-breakpoint
|
||||
ALTER TABLE `backup_schedules_table` ADD `organization_id` text REFERENCES organization(id);--> statement-breakpoint
|
||||
ALTER TABLE `notification_destinations_table` ADD `organization_id` text REFERENCES organization(id);--> statement-breakpoint
|
||||
ALTER TABLE `repositories_table` ADD `organization_id` text REFERENCES organization(id);
|
||||
22
app/drizzle/0039_backfill-entities-org-id.sql
Normal file
22
app/drizzle/0039_backfill-entities-org-id.sql
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
-- Backfill organization_id for volumes, backups, notifications and repositories
|
||||
-- Uses the first organization found in the database
|
||||
|
||||
-- Update volumes_table
|
||||
UPDATE volumes_table
|
||||
SET organization_id = (SELECT id FROM organization ORDER BY created_at ASC LIMIT 1)
|
||||
WHERE organization_id IS NULL;
|
||||
|
||||
-- Update backup_schedules_table
|
||||
UPDATE backup_schedules_table
|
||||
SET organization_id = (SELECT id FROM organization ORDER BY created_at ASC LIMIT 1)
|
||||
WHERE organization_id IS NULL;
|
||||
|
||||
-- Update notification_destinations_table
|
||||
UPDATE notification_destinations_table
|
||||
SET organization_id = (SELECT id FROM organization ORDER BY created_at ASC LIMIT 1)
|
||||
WHERE organization_id IS NULL;
|
||||
|
||||
-- Update repositories_table
|
||||
UPDATE repositories_table
|
||||
SET organization_id = (SELECT id FROM organization ORDER BY created_at ASC LIMIT 1)
|
||||
WHERE organization_id IS NULL;
|
||||
1626
app/drizzle/meta/0038_snapshot.json
Normal file
1626
app/drizzle/meta/0038_snapshot.json
Normal file
File diff suppressed because it is too large
Load diff
1626
app/drizzle/meta/0039_snapshot.json
Normal file
1626
app/drizzle/meta/0039_snapshot.json
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -267,6 +267,20 @@
|
|||
"when": 1768654184173,
|
||||
"tag": "0037_create-default-member",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 38,
|
||||
"version": "6",
|
||||
"when": 1768658258128,
|
||||
"tag": "0038_shallow_pride",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 39,
|
||||
"version": "6",
|
||||
"when": 1768658359845,
|
||||
"tag": "0039_backfill-entities-org-id",
|
||||
"breakpoints": true
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -169,7 +169,7 @@ export const invitation = sqliteTable(
|
|||
export const volumesTable = sqliteTable("volumes_table", {
|
||||
id: int().primaryKey({ autoIncrement: true }),
|
||||
shortId: text("short_id").notNull().unique(),
|
||||
name: text().notNull().unique(),
|
||||
name: text().notNull(),
|
||||
type: text().$type<BackendType>().notNull(),
|
||||
status: text().$type<BackendStatus>().notNull().default("unmounted"),
|
||||
lastError: text("last_error"),
|
||||
|
|
@ -185,6 +185,7 @@ export const volumesTable = sqliteTable("volumes_table", {
|
|||
.default(sql`(unixepoch() * 1000)`),
|
||||
config: text("config", { mode: "json" }).$type<typeof volumeConfigSchema.inferOut>().notNull(),
|
||||
autoRemount: int("auto_remount", { mode: "boolean" }).notNull().default(true),
|
||||
organizationId: text("organization_id").references(() => organization.id, { onDelete: "cascade" }),
|
||||
});
|
||||
export type Volume = typeof volumesTable.$inferSelect;
|
||||
export type VolumeInsert = typeof volumesTable.$inferInsert;
|
||||
|
|
@ -214,6 +215,7 @@ export const repositoriesTable = sqliteTable("repositories_table", {
|
|||
updatedAt: int("updated_at", { mode: "number" })
|
||||
.notNull()
|
||||
.default(sql`(unixepoch() * 1000)`),
|
||||
organizationId: text("organization_id").references(() => organization.id, { onDelete: "cascade" }),
|
||||
});
|
||||
export type Repository = typeof repositoriesTable.$inferSelect;
|
||||
export type RepositoryInsert = typeof repositoriesTable.$inferInsert;
|
||||
|
|
@ -257,6 +259,7 @@ export const backupSchedulesTable = sqliteTable("backup_schedules_table", {
|
|||
updatedAt: int("updated_at", { mode: "number" })
|
||||
.notNull()
|
||||
.default(sql`(unixepoch() * 1000)`),
|
||||
organizationId: text("organization_id").references(() => organization.id, { onDelete: "cascade" }),
|
||||
});
|
||||
export type BackupScheduleInsert = typeof backupSchedulesTable.$inferInsert;
|
||||
|
||||
|
|
@ -277,6 +280,7 @@ export const notificationDestinationsTable = sqliteTable("notification_destinati
|
|||
updatedAt: int("updated_at", { mode: "number" })
|
||||
.notNull()
|
||||
.default(sql`(unixepoch() * 1000)`),
|
||||
organizationId: text("organization_id").references(() => organization.id, { onDelete: "cascade" }),
|
||||
});
|
||||
export type NotificationDestination = typeof notificationDestinationsTable.$inferSelect;
|
||||
|
||||
|
|
@ -373,6 +377,10 @@ export const sessionRelations = relations(sessionsTable, ({ one }) => ({
|
|||
fields: [sessionsTable.userId],
|
||||
references: [usersTable.id],
|
||||
}),
|
||||
activeOrganization: one(organization, {
|
||||
fields: [sessionsTable.activeOrganizationId],
|
||||
references: [organization.id],
|
||||
}),
|
||||
}));
|
||||
|
||||
export const accountRelations = relations(account, ({ one }) => ({
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ declare module "hono" {
|
|||
username: string;
|
||||
hasDownloadedResticPassword: boolean;
|
||||
};
|
||||
organizationId: string;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -16,17 +17,19 @@ declare module "hono" {
|
|||
* Verifies the session cookie and attaches user to context
|
||||
*/
|
||||
export const requireAuth = createMiddleware(async (c, next) => {
|
||||
const session = await auth.api.getSession({
|
||||
const sess = await auth.api.getSession({
|
||||
headers: c.req.raw.headers,
|
||||
});
|
||||
|
||||
const { user } = session ?? {};
|
||||
const { user, session } = sess ?? {};
|
||||
const { activeOrganizationId } = session ?? {};
|
||||
|
||||
if (!user) {
|
||||
if (!user || !session || !activeOrganizationId) {
|
||||
return c.json<unknown>({ message: "Invalid or expired session" }, 401);
|
||||
}
|
||||
|
||||
c.set("user", user);
|
||||
c.set("organizationId", activeOrganizationId);
|
||||
|
||||
await next();
|
||||
});
|
||||
|
|
|
|||
|
|
@ -29,13 +29,13 @@ import { requireAuth } from "../auth/auth.middleware";
|
|||
export const volumeController = new Hono()
|
||||
.use(requireAuth)
|
||||
.get("/", listVolumesDto, async (c) => {
|
||||
const volumes = await volumeService.listVolumes();
|
||||
const volumes = await volumeService.listVolumes(c.get("organizationId"));
|
||||
|
||||
return c.json<ListVolumesDto>(volumes, 200);
|
||||
})
|
||||
.post("/", createVolumeDto, validator("json", createVolumeBody), async (c) => {
|
||||
const body = c.req.valid("json");
|
||||
const res = await volumeService.createVolume(body.name, body.config);
|
||||
const res = await volumeService.createVolume(body.name, body.config, c.get("organizationId"));
|
||||
|
||||
const response = {
|
||||
...res.volume,
|
||||
|
|
|
|||
|
|
@ -42,13 +42,15 @@ async function encryptSensitiveFields(config: BackendConfig): Promise<BackendCon
|
|||
}
|
||||
}
|
||||
|
||||
const listVolumes = async () => {
|
||||
const volumes = await db.query.volumesTable.findMany({});
|
||||
const listVolumes = async (organizationId: string) => {
|
||||
const volumes = await db.query.volumesTable.findMany({
|
||||
where: eq(volumesTable.organizationId, organizationId),
|
||||
});
|
||||
|
||||
return volumes;
|
||||
};
|
||||
|
||||
const createVolume = async (name: string, backendConfig: BackendConfig) => {
|
||||
const createVolume = async (name: string, backendConfig: BackendConfig, organizationId: string) => {
|
||||
const slug = slugify(name, { lower: true, strict: true });
|
||||
|
||||
const existing = await db.query.volumesTable.findFirst({
|
||||
|
|
@ -69,6 +71,7 @@ const createVolume = async (name: string, backendConfig: BackendConfig) => {
|
|||
name: slug,
|
||||
config: encryptedConfig,
|
||||
type: backendConfig.backend,
|
||||
organizationId,
|
||||
})
|
||||
.returning();
|
||||
|
||||
|
|
@ -252,6 +255,7 @@ const testConnection = async (backendConfig: BackendConfig) => {
|
|||
status: "unmounted" as const,
|
||||
lastError: null,
|
||||
autoRemount: true,
|
||||
organizationId: "test-org",
|
||||
};
|
||||
|
||||
const backend = createVolumeBackend(mockVolume);
|
||||
|
|
|
|||
Loading…
Reference in a new issue