mirror of
https://github.com/withastro/astro.git
synced 2025-03-03 22:57:08 -05:00
migrate seed data to new db push command
This commit is contained in:
parent
962c425ceb
commit
f85e8cd897
3 changed files with 73 additions and 49 deletions
|
@ -1,24 +1,25 @@
|
||||||
|
import type { InArgs, InStatement } from '@libsql/client';
|
||||||
import type { AstroConfig } from 'astro';
|
import type { AstroConfig } from 'astro';
|
||||||
import deepDiff from 'deep-diff';
|
import deepDiff from 'deep-diff';
|
||||||
import { eq, sql } from 'drizzle-orm';
|
import { eq, sql, type Query } from 'drizzle-orm';
|
||||||
import { readFile } from 'fs/promises';
|
|
||||||
import type { Arguments } from 'yargs-parser';
|
import type { Arguments } from 'yargs-parser';
|
||||||
import { appTokenError } from '../../../errors.js';
|
import { appTokenError } from '../../../errors.js';
|
||||||
import {
|
import { collectionToTable, createLocalDatabaseClient } from '../../../internal.js';
|
||||||
getMigrations,
|
import { getMigrations, initializeFromMigrations, loadMigration } from '../../../migrations.js';
|
||||||
initializeFromMigrations,
|
import type { DBCollections } from '../../../types.js';
|
||||||
} from '../../../migrations.js';
|
|
||||||
import {
|
import {
|
||||||
STUDIO_ADMIN_TABLE_ROW_ID,
|
STUDIO_ADMIN_TABLE_ROW_ID,
|
||||||
adminTable,
|
adminTable,
|
||||||
createRemoteDatabaseClient,
|
createRemoteDatabaseClient,
|
||||||
getAstroStudioEnv
|
getAstroStudioEnv,
|
||||||
|
getRemoteDatabaseUrl,
|
||||||
} from '../../../utils.js';
|
} from '../../../utils.js';
|
||||||
const { diff } = deepDiff;
|
const { diff } = deepDiff;
|
||||||
|
|
||||||
|
export async function cmd({ config, flags }: { config: AstroConfig; flags: Arguments }) {
|
||||||
|
const isSeedData = flags.seed;
|
||||||
|
const isDryRun = flags.dryRun;
|
||||||
|
|
||||||
|
|
||||||
export async function cmd({ config }: { config: AstroConfig, flags: Arguments }) {
|
|
||||||
const currentSnapshot = JSON.parse(JSON.stringify(config.db?.collections ?? {}));
|
const currentSnapshot = JSON.parse(JSON.stringify(config.db?.collections ?? {}));
|
||||||
const allMigrationFiles = await getMigrations();
|
const allMigrationFiles = await getMigrations();
|
||||||
if (allMigrationFiles.length === 0) {
|
if (allMigrationFiles.length === 0) {
|
||||||
|
@ -50,23 +51,74 @@ export async function cmd({ config }: { config: AstroConfig, flags: Arguments })
|
||||||
const missingMigrations = allLocalMigrations.filter((migration) => {
|
const missingMigrations = allLocalMigrations.filter((migration) => {
|
||||||
return !allRemoteMigrations.find((m: any) => m.name === migration);
|
return !allRemoteMigrations.find((m: any) => m.name === migration);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
console.log(`Pushing ${missingMigrations.length} migrations...`);
|
||||||
|
|
||||||
// load all missing migrations
|
// load all missing migrations
|
||||||
const missingMigrationContents = await Promise.all(
|
const missingMigrationContents = await Promise.all(missingMigrations.map(loadMigration));
|
||||||
missingMigrations.map(async (migration) => {
|
|
||||||
return JSON.parse(await readFile(`./migrations/${migration}`, 'utf-8'));
|
|
||||||
})
|
|
||||||
);
|
|
||||||
// combine all missing migrations into a single batch
|
// combine all missing migrations into a single batch
|
||||||
const missingMigrationBatch = missingMigrationContents.reduce((acc, curr) => {
|
const missingMigrationBatch = missingMigrationContents.reduce((acc, curr) => {
|
||||||
return [...acc, ...curr.diff];
|
return [...acc, ...curr.db];
|
||||||
});
|
}, [] as string[]);
|
||||||
// apply the batch to the DB
|
// apply the batch to the DB
|
||||||
// TODO: How to do this with Drizzle ORM & proxy implementation? Unclear.
|
// TODO: How to do this with Drizzle ORM & proxy implementation? Unclear.
|
||||||
// @ts-expect-error
|
// @ts-expect-error
|
||||||
await db.batch(missingMigrationBatch);
|
await db.batch(missingMigrationBatch);
|
||||||
|
|
||||||
|
// TODO: Update the migrations table to set all to "applied"
|
||||||
|
|
||||||
// update the config schema in the admin table
|
// update the config schema in the admin table
|
||||||
db.update(adminTable)
|
db.update(adminTable)
|
||||||
.set({ collections: JSON.stringify(currentSnapshot) })
|
.set({ collections: JSON.stringify(currentSnapshot) })
|
||||||
.where(eq(adminTable.id, STUDIO_ADMIN_TABLE_ROW_ID));
|
.where(eq(adminTable.id, STUDIO_ADMIN_TABLE_ROW_ID));
|
||||||
|
|
||||||
|
if (isSeedData) {
|
||||||
|
console.info('Pushing data...');
|
||||||
|
await tempDataPush({ currentSnapshot, appToken, isDryRun });
|
||||||
|
}
|
||||||
|
console.info('Push complete!');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** TODO: refine with migration changes */
|
||||||
|
async function tempDataPush({
|
||||||
|
currentSnapshot,
|
||||||
|
appToken,
|
||||||
|
isDryRun,
|
||||||
|
}: {
|
||||||
|
currentSnapshot: DBCollections;
|
||||||
|
appToken: string;
|
||||||
|
isDryRun?: boolean;
|
||||||
|
}) {
|
||||||
|
const db = await createLocalDatabaseClient({
|
||||||
|
collections: currentSnapshot,
|
||||||
|
dbUrl: ':memory:',
|
||||||
|
seeding: true,
|
||||||
|
});
|
||||||
|
const queries: Query[] = [];
|
||||||
|
|
||||||
|
for (const [name, collection] of Object.entries(currentSnapshot)) {
|
||||||
|
if (collection.writable || !collection.data) continue;
|
||||||
|
const table = collectionToTable(name, collection);
|
||||||
|
const insert = db.insert(table).values(await collection.data());
|
||||||
|
|
||||||
|
queries.push(insert.toSQL());
|
||||||
|
}
|
||||||
|
const url = new URL('/db/query', getRemoteDatabaseUrl());
|
||||||
|
const requestBody: InStatement[] = queries.map((q) => ({
|
||||||
|
sql: q.sql,
|
||||||
|
args: q.params as InArgs,
|
||||||
|
}));
|
||||||
|
|
||||||
|
if (isDryRun) {
|
||||||
|
console.info('[DRY RUN] Batch data seed:', JSON.stringify(requestBody, null, 2));
|
||||||
|
return new Response(null, { status: 200 });
|
||||||
|
}
|
||||||
|
|
||||||
|
return await fetch(url, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: new Headers({
|
||||||
|
Authorization: `Bearer ${appToken}`,
|
||||||
|
}),
|
||||||
|
body: JSON.stringify(requestBody),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import deepDiff from 'deep-diff';
|
import deepDiff from 'deep-diff';
|
||||||
import { mkdir, readFile, readdir, writeFile } from 'fs/promises';
|
import { mkdir, readFile, readdir, writeFile } from 'fs/promises';
|
||||||
const { diff, applyChange } = deepDiff;
|
import type { DBCollections } from './types.js';
|
||||||
|
const { applyChange } = deepDiff;
|
||||||
|
|
||||||
export async function getMigrations(): Promise<string[]> {
|
export async function getMigrations(): Promise<string[]> {
|
||||||
const migrationFiles = await readdir('./migrations').catch((err) => {
|
const migrationFiles = await readdir('./migrations').catch((err) => {
|
||||||
|
@ -12,11 +13,11 @@ export async function getMigrations(): Promise<string[]> {
|
||||||
return migrationFiles;
|
return migrationFiles;
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function loadMigration(migration: string): Promise<{ diff: any[]; db: any[] }> {
|
export async function loadMigration(migration: string): Promise<{ diff: any[]; db: string[] }> {
|
||||||
return JSON.parse(await readFile(`./migrations/${migration}`, 'utf-8'));
|
return JSON.parse(await readFile(`./migrations/${migration}`, 'utf-8'));
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function loadInitialSnapshot(): Promise<any> {
|
export async function loadInitialSnapshot(): Promise<DBCollections> {
|
||||||
return JSON.parse(await readFile('./migrations/0000_snapshot.json', 'utf-8'));
|
return JSON.parse(await readFile('./migrations/0000_snapshot.json', 'utf-8'));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -25,7 +26,7 @@ export async function initializeMigrationsDirectory(currentSnapshot: unknown) {
|
||||||
await writeFile('./migrations/0000_snapshot.json', JSON.stringify(currentSnapshot, undefined, 2));
|
await writeFile('./migrations/0000_snapshot.json', JSON.stringify(currentSnapshot, undefined, 2));
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function initializeFromMigrations(allMigrationFiles: string[]) {
|
export async function initializeFromMigrations(allMigrationFiles: string[]): Promise<DBCollections> {
|
||||||
const prevSnapshot = await loadInitialSnapshot();
|
const prevSnapshot = await loadInitialSnapshot();
|
||||||
for (const migration of allMigrationFiles) {
|
for (const migration of allMigrationFiles) {
|
||||||
if (migration === '0000_snapshot.json') continue;
|
if (migration === '0000_snapshot.json') continue;
|
||||||
|
|
|
@ -2,7 +2,6 @@ import type { InStatement } from "@libsql/client";
|
||||||
import type { AstroConfig } from 'astro';
|
import type { AstroConfig } from 'astro';
|
||||||
import { sqliteTable, text } from 'drizzle-orm/sqlite-core';
|
import { sqliteTable, text } from 'drizzle-orm/sqlite-core';
|
||||||
import { drizzle } from 'drizzle-orm/sqlite-proxy';
|
import { drizzle } from 'drizzle-orm/sqlite-proxy';
|
||||||
import { red, yellow } from 'kleur/colors';
|
|
||||||
import { loadEnv } from 'vite';
|
import { loadEnv } from 'vite';
|
||||||
import { z } from 'zod';
|
import { z } from 'zod';
|
||||||
|
|
||||||
|
@ -22,34 +21,6 @@ export function getAstroStudioEnv(envMode = ''): Record<`ASTRO_STUDIO_${string}`
|
||||||
return env;
|
return env;
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function isAppTokenValid({
|
|
||||||
remoteDbUrl,
|
|
||||||
appToken,
|
|
||||||
}: {
|
|
||||||
remoteDbUrl: string;
|
|
||||||
appToken: string;
|
|
||||||
}): Promise<boolean> {
|
|
||||||
const { status } = await fetch(new URL('/authorize', remoteDbUrl), {
|
|
||||||
headers: {
|
|
||||||
Authorization: `Bearer ${appToken}`,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
if (status === 200) {
|
|
||||||
return true;
|
|
||||||
} else if (status === 401) {
|
|
||||||
// eslint-disable-next-line no-console
|
|
||||||
console.warn(yellow(`⚠️ App token is invalid or revoked.`));
|
|
||||||
return false;
|
|
||||||
} else {
|
|
||||||
// eslint-disable-next-line no-console
|
|
||||||
console.error(
|
|
||||||
`${red('⚠️ Unexpected error connecting to Astro Studio.')} Please try again later.`,
|
|
||||||
);
|
|
||||||
process.exit(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getStudioUrl(): string {
|
export function getStudioUrl(): string {
|
||||||
const env = getAstroStudioEnv();
|
const env = getAstroStudioEnv();
|
||||||
return env.ASTRO_STUDIO_BASE_URL;
|
return env.ASTRO_STUDIO_BASE_URL;
|
||||||
|
|
Loading…
Add table
Reference in a new issue