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,
|
"when": 1768654184173,
|
||||||
"tag": "0037_create-default-member",
|
"tag": "0037_create-default-member",
|
||||||
"breakpoints": true
|
"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", {
|
export const volumesTable = sqliteTable("volumes_table", {
|
||||||
id: int().primaryKey({ autoIncrement: true }),
|
id: int().primaryKey({ autoIncrement: true }),
|
||||||
shortId: text("short_id").notNull().unique(),
|
shortId: text("short_id").notNull().unique(),
|
||||||
name: text().notNull().unique(),
|
name: text().notNull(),
|
||||||
type: text().$type<BackendType>().notNull(),
|
type: text().$type<BackendType>().notNull(),
|
||||||
status: text().$type<BackendStatus>().notNull().default("unmounted"),
|
status: text().$type<BackendStatus>().notNull().default("unmounted"),
|
||||||
lastError: text("last_error"),
|
lastError: text("last_error"),
|
||||||
|
|
@ -185,6 +185,7 @@ export const volumesTable = sqliteTable("volumes_table", {
|
||||||
.default(sql`(unixepoch() * 1000)`),
|
.default(sql`(unixepoch() * 1000)`),
|
||||||
config: text("config", { mode: "json" }).$type<typeof volumeConfigSchema.inferOut>().notNull(),
|
config: text("config", { mode: "json" }).$type<typeof volumeConfigSchema.inferOut>().notNull(),
|
||||||
autoRemount: int("auto_remount", { mode: "boolean" }).notNull().default(true),
|
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 Volume = typeof volumesTable.$inferSelect;
|
||||||
export type VolumeInsert = typeof volumesTable.$inferInsert;
|
export type VolumeInsert = typeof volumesTable.$inferInsert;
|
||||||
|
|
@ -214,6 +215,7 @@ export const repositoriesTable = sqliteTable("repositories_table", {
|
||||||
updatedAt: int("updated_at", { mode: "number" })
|
updatedAt: int("updated_at", { mode: "number" })
|
||||||
.notNull()
|
.notNull()
|
||||||
.default(sql`(unixepoch() * 1000)`),
|
.default(sql`(unixepoch() * 1000)`),
|
||||||
|
organizationId: text("organization_id").references(() => organization.id, { onDelete: "cascade" }),
|
||||||
});
|
});
|
||||||
export type Repository = typeof repositoriesTable.$inferSelect;
|
export type Repository = typeof repositoriesTable.$inferSelect;
|
||||||
export type RepositoryInsert = typeof repositoriesTable.$inferInsert;
|
export type RepositoryInsert = typeof repositoriesTable.$inferInsert;
|
||||||
|
|
@ -257,6 +259,7 @@ export const backupSchedulesTable = sqliteTable("backup_schedules_table", {
|
||||||
updatedAt: int("updated_at", { mode: "number" })
|
updatedAt: int("updated_at", { mode: "number" })
|
||||||
.notNull()
|
.notNull()
|
||||||
.default(sql`(unixepoch() * 1000)`),
|
.default(sql`(unixepoch() * 1000)`),
|
||||||
|
organizationId: text("organization_id").references(() => organization.id, { onDelete: "cascade" }),
|
||||||
});
|
});
|
||||||
export type BackupScheduleInsert = typeof backupSchedulesTable.$inferInsert;
|
export type BackupScheduleInsert = typeof backupSchedulesTable.$inferInsert;
|
||||||
|
|
||||||
|
|
@ -277,6 +280,7 @@ export const notificationDestinationsTable = sqliteTable("notification_destinati
|
||||||
updatedAt: int("updated_at", { mode: "number" })
|
updatedAt: int("updated_at", { mode: "number" })
|
||||||
.notNull()
|
.notNull()
|
||||||
.default(sql`(unixepoch() * 1000)`),
|
.default(sql`(unixepoch() * 1000)`),
|
||||||
|
organizationId: text("organization_id").references(() => organization.id, { onDelete: "cascade" }),
|
||||||
});
|
});
|
||||||
export type NotificationDestination = typeof notificationDestinationsTable.$inferSelect;
|
export type NotificationDestination = typeof notificationDestinationsTable.$inferSelect;
|
||||||
|
|
||||||
|
|
@ -373,6 +377,10 @@ export const sessionRelations = relations(sessionsTable, ({ one }) => ({
|
||||||
fields: [sessionsTable.userId],
|
fields: [sessionsTable.userId],
|
||||||
references: [usersTable.id],
|
references: [usersTable.id],
|
||||||
}),
|
}),
|
||||||
|
activeOrganization: one(organization, {
|
||||||
|
fields: [sessionsTable.activeOrganizationId],
|
||||||
|
references: [organization.id],
|
||||||
|
}),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
export const accountRelations = relations(account, ({ one }) => ({
|
export const accountRelations = relations(account, ({ one }) => ({
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@ declare module "hono" {
|
||||||
username: string;
|
username: string;
|
||||||
hasDownloadedResticPassword: boolean;
|
hasDownloadedResticPassword: boolean;
|
||||||
};
|
};
|
||||||
|
organizationId: string;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -16,17 +17,19 @@ declare module "hono" {
|
||||||
* Verifies the session cookie and attaches user to context
|
* Verifies the session cookie and attaches user to context
|
||||||
*/
|
*/
|
||||||
export const requireAuth = createMiddleware(async (c, next) => {
|
export const requireAuth = createMiddleware(async (c, next) => {
|
||||||
const session = await auth.api.getSession({
|
const sess = await auth.api.getSession({
|
||||||
headers: c.req.raw.headers,
|
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);
|
return c.json<unknown>({ message: "Invalid or expired session" }, 401);
|
||||||
}
|
}
|
||||||
|
|
||||||
c.set("user", user);
|
c.set("user", user);
|
||||||
|
c.set("organizationId", activeOrganizationId);
|
||||||
|
|
||||||
await next();
|
await next();
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -29,13 +29,13 @@ import { requireAuth } from "../auth/auth.middleware";
|
||||||
export const volumeController = new Hono()
|
export const volumeController = new Hono()
|
||||||
.use(requireAuth)
|
.use(requireAuth)
|
||||||
.get("/", listVolumesDto, async (c) => {
|
.get("/", listVolumesDto, async (c) => {
|
||||||
const volumes = await volumeService.listVolumes();
|
const volumes = await volumeService.listVolumes(c.get("organizationId"));
|
||||||
|
|
||||||
return c.json<ListVolumesDto>(volumes, 200);
|
return c.json<ListVolumesDto>(volumes, 200);
|
||||||
})
|
})
|
||||||
.post("/", createVolumeDto, validator("json", createVolumeBody), async (c) => {
|
.post("/", createVolumeDto, validator("json", createVolumeBody), async (c) => {
|
||||||
const body = c.req.valid("json");
|
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 = {
|
const response = {
|
||||||
...res.volume,
|
...res.volume,
|
||||||
|
|
|
||||||
|
|
@ -42,13 +42,15 @@ async function encryptSensitiveFields(config: BackendConfig): Promise<BackendCon
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const listVolumes = async () => {
|
const listVolumes = async (organizationId: string) => {
|
||||||
const volumes = await db.query.volumesTable.findMany({});
|
const volumes = await db.query.volumesTable.findMany({
|
||||||
|
where: eq(volumesTable.organizationId, organizationId),
|
||||||
|
});
|
||||||
|
|
||||||
return volumes;
|
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 slug = slugify(name, { lower: true, strict: true });
|
||||||
|
|
||||||
const existing = await db.query.volumesTable.findFirst({
|
const existing = await db.query.volumesTable.findFirst({
|
||||||
|
|
@ -69,6 +71,7 @@ const createVolume = async (name: string, backendConfig: BackendConfig) => {
|
||||||
name: slug,
|
name: slug,
|
||||||
config: encryptedConfig,
|
config: encryptedConfig,
|
||||||
type: backendConfig.backend,
|
type: backendConfig.backend,
|
||||||
|
organizationId,
|
||||||
})
|
})
|
||||||
.returning();
|
.returning();
|
||||||
|
|
||||||
|
|
@ -252,6 +255,7 @@ const testConnection = async (backendConfig: BackendConfig) => {
|
||||||
status: "unmounted" as const,
|
status: "unmounted" as const,
|
||||||
lastError: null,
|
lastError: null,
|
||||||
autoRemount: true,
|
autoRemount: true,
|
||||||
|
organizationId: "test-org",
|
||||||
};
|
};
|
||||||
|
|
||||||
const backend = createVolumeBackend(mockVolume);
|
const backend = createVolumeBackend(mockVolume);
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue