@studiocms/migrator 0.0.0-beta.0 → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +63 -0
- package/package.json +55 -7
- package/public/favicon.svg +5 -0
- package/src/components/ErrorCard.astro +20 -0
- package/src/components/MigrationMeta.astro +74 -0
- package/src/components/MigrationStatus.astro +71 -0
- package/src/components/PageHeader.astro +24 -0
- package/src/components/StatusTables.astro +45 -0
- package/src/db/astro-db-drizzle-client.ts +14 -0
- package/src/db/astro-db-schema.ts +193 -0
- package/src/db/astrodb.ts +88 -0
- package/src/db/client.ts +156 -0
- package/src/db/drizzle-schema.ts +54 -0
- package/src/env.d.ts +3 -0
- package/src/fonts/css/onest-variable.css +16 -0
- package/src/fonts/woff2/onest-variable.woff2 +0 -0
- package/src/layouts/Layout.astro +30 -0
- package/src/lib/astro-db-drizzle-compat/core-types.ts +88 -0
- package/src/lib/astro-db-drizzle-compat/error-map.ts +105 -0
- package/src/lib/astro-db-drizzle-compat/index.ts +149 -0
- package/src/lib/astro-db-drizzle-compat/schemas.ts +249 -0
- package/src/lib/astro-db-drizzle-compat/types.ts +141 -0
- package/src/lib/astro-db-drizzle-compat/utils.ts +55 -0
- package/src/lib/astro-db-drizzle-compat/virtual.ts +91 -0
- package/src/lib/errors.ts +57 -0
- package/src/lib/logger.ts +4 -0
- package/src/lib/remapUtils.ts +170 -0
- package/src/lib/response-utils.ts +12 -0
- package/src/lib/tableMap.ts +236 -0
- package/src/pages/data-migrations.ts +268 -0
- package/src/pages/index.astro +259 -0
- package/src/pages/schema-migrations.ts +31 -0
- package/src/styles/global.css +165 -0
- package/start.mjs +163 -0
- package/utils/logger.mjs +93 -0
- package/utils/resolver.mjs +60 -0
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
import type { AstroDBTableKeys, GetMigrationTypes } from './tableMap.js';
|
|
2
|
+
|
|
3
|
+
type Users = GetMigrationTypes<'StudioCMSUsers'>;
|
|
4
|
+
type PageData = GetMigrationTypes<'StudioCMSPageData'>;
|
|
5
|
+
type PageFolders = GetMigrationTypes<'StudioCMSPageFolderStructure'>;
|
|
6
|
+
type PageDataTags = GetMigrationTypes<'StudioCMSPageDataTags'>;
|
|
7
|
+
type PageDataCategories = GetMigrationTypes<'StudioCMSPageDataCategories'>;
|
|
8
|
+
type PluginData = GetMigrationTypes<'StudioCMSPluginData'>;
|
|
9
|
+
type DynamicConfig = GetMigrationTypes<'StudioCMSDynamicConfigSettings'>;
|
|
10
|
+
type apiKeys = GetMigrationTypes<'StudioCMSAPIKeys'>;
|
|
11
|
+
type diffTracking = GetMigrationTypes<'StudioCMSDiffTracking'>;
|
|
12
|
+
type emailVerificationTokens = GetMigrationTypes<'StudioCMSEmailVerificationTokens'>;
|
|
13
|
+
type oAuthAccounts = GetMigrationTypes<'StudioCMSOAuthAccounts'>;
|
|
14
|
+
type pageContent = GetMigrationTypes<'StudioCMSPageContent'>;
|
|
15
|
+
type permissions = GetMigrationTypes<'StudioCMSPermissions'>;
|
|
16
|
+
type sessionTable = GetMigrationTypes<'StudioCMSSessionTable'>;
|
|
17
|
+
type userResetTokens = GetMigrationTypes<'StudioCMSUserResetTokens'>;
|
|
18
|
+
|
|
19
|
+
const booleanToNumber = (value: boolean): number => (value ? 1 : 0);
|
|
20
|
+
|
|
21
|
+
export const remapUsersTable = (astro: Users['astro'][]): Users['kysely'][] => {
|
|
22
|
+
return astro.map((user) => ({
|
|
23
|
+
...user,
|
|
24
|
+
// Add any necessary transformations here
|
|
25
|
+
createdAt: user.createdAt?.toISOString(),
|
|
26
|
+
updatedAt: new Date().toISOString(),
|
|
27
|
+
emailVerified: booleanToNumber(user.emailVerified),
|
|
28
|
+
}));
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
export const remapPageDataTable = (astro: PageData['astro'][]): PageData['kysely'][] => {
|
|
32
|
+
return astro.map((item) => ({
|
|
33
|
+
...item,
|
|
34
|
+
// Add any necessary transformations here
|
|
35
|
+
updatedAt: new Date().toISOString(),
|
|
36
|
+
showOnNav: booleanToNumber(item.showOnNav),
|
|
37
|
+
showAuthor: booleanToNumber(item.showAuthor ?? false),
|
|
38
|
+
showContributors: booleanToNumber(item.showContributors ?? false),
|
|
39
|
+
publishedAt: item.publishedAt?.toISOString(),
|
|
40
|
+
categories: JSON.stringify(item.categories),
|
|
41
|
+
tags: JSON.stringify(item.tags),
|
|
42
|
+
contributorIds: JSON.stringify(item.contributorIds),
|
|
43
|
+
augments: JSON.stringify([]),
|
|
44
|
+
authorId: item.authorId ?? '',
|
|
45
|
+
draft: booleanToNumber(item.draft ?? false),
|
|
46
|
+
}));
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
export const remapPageFoldersTable = (astro: PageFolders['astro'][]): PageFolders['kysely'][] => {
|
|
50
|
+
return astro.map((item) => ({
|
|
51
|
+
...item,
|
|
52
|
+
}));
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
export const remapPageDataTagsTable = (
|
|
56
|
+
astro: PageDataTags['astro'][]
|
|
57
|
+
): PageDataTags['kysely'][] => {
|
|
58
|
+
return astro.map((item) => ({
|
|
59
|
+
...item,
|
|
60
|
+
meta: JSON.stringify(item.meta),
|
|
61
|
+
}));
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
export const remapPageDataCategoriesTable = (
|
|
65
|
+
astro: PageDataCategories['astro'][]
|
|
66
|
+
): PageDataCategories['kysely'][] => {
|
|
67
|
+
return astro.map((item) => ({
|
|
68
|
+
...item,
|
|
69
|
+
meta: JSON.stringify(item.meta),
|
|
70
|
+
}));
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
export const remapPluginDataTable = (astro: PluginData['astro'][]): PluginData['kysely'][] => {
|
|
74
|
+
return astro.map((item) => ({
|
|
75
|
+
...item,
|
|
76
|
+
data: JSON.stringify(item.data),
|
|
77
|
+
}));
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
export const remapDynamicConfigTable = (
|
|
81
|
+
astro: DynamicConfig['astro'][]
|
|
82
|
+
): DynamicConfig['kysely'][] => {
|
|
83
|
+
return astro.map((item) => ({
|
|
84
|
+
...item,
|
|
85
|
+
data: JSON.stringify(item.data),
|
|
86
|
+
}));
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
export const remapApiKeysTable = (astro: apiKeys['astro'][]): apiKeys['kysely'][] => {
|
|
90
|
+
return astro.map((item) => ({
|
|
91
|
+
...item,
|
|
92
|
+
creationDate: item.creationDate?.toISOString(),
|
|
93
|
+
}));
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
export const remapDiffTrackingTable = (
|
|
97
|
+
astro: diffTracking['astro'][]
|
|
98
|
+
): diffTracking['kysely'][] => {
|
|
99
|
+
return astro.map((item) => ({
|
|
100
|
+
...item,
|
|
101
|
+
pageMetaData: JSON.stringify(item.pageMetaData),
|
|
102
|
+
timestamp: item.timestamp?.toISOString(),
|
|
103
|
+
}));
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
export const remapEmailVerificationTokensTable = (
|
|
107
|
+
astro: emailVerificationTokens['astro'][]
|
|
108
|
+
): emailVerificationTokens['kysely'][] => {
|
|
109
|
+
return astro.map((item) => ({
|
|
110
|
+
...item,
|
|
111
|
+
expiresAt: item.expiresAt?.toISOString(),
|
|
112
|
+
}));
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
export const remapOAuthAccountsTable = (
|
|
116
|
+
astro: oAuthAccounts['astro'][]
|
|
117
|
+
): oAuthAccounts['kysely'][] => {
|
|
118
|
+
return astro.map((item) => ({
|
|
119
|
+
...item,
|
|
120
|
+
}));
|
|
121
|
+
};
|
|
122
|
+
|
|
123
|
+
export const remapPageContentTable = (astro: pageContent['astro'][]): pageContent['kysely'][] => {
|
|
124
|
+
return astro.map((item) => ({
|
|
125
|
+
...item,
|
|
126
|
+
content: item.content ?? '',
|
|
127
|
+
}));
|
|
128
|
+
};
|
|
129
|
+
|
|
130
|
+
export const remapPermissionsTable = (astro: permissions['astro'][]): permissions['kysely'][] => {
|
|
131
|
+
return astro.map(({ rank, user }) => ({
|
|
132
|
+
rank,
|
|
133
|
+
user,
|
|
134
|
+
}));
|
|
135
|
+
};
|
|
136
|
+
|
|
137
|
+
export const remapSessionTable = (astro: sessionTable['astro'][]): sessionTable['kysely'][] => {
|
|
138
|
+
return astro.map((item) => ({
|
|
139
|
+
...item,
|
|
140
|
+
expiresAt: item.expiresAt?.toISOString(),
|
|
141
|
+
}));
|
|
142
|
+
};
|
|
143
|
+
|
|
144
|
+
export const remapUserResetTokensTable = (
|
|
145
|
+
astro: userResetTokens['astro'][]
|
|
146
|
+
): userResetTokens['kysely'][] => {
|
|
147
|
+
return astro;
|
|
148
|
+
};
|
|
149
|
+
|
|
150
|
+
export const remapFunctions: {
|
|
151
|
+
[key in AstroDBTableKeys]: (
|
|
152
|
+
astro: GetMigrationTypes<key>['astro'][]
|
|
153
|
+
) => GetMigrationTypes<key>['kysely'][];
|
|
154
|
+
} = {
|
|
155
|
+
StudioCMSUsers: remapUsersTable,
|
|
156
|
+
StudioCMSPageData: remapPageDataTable,
|
|
157
|
+
StudioCMSPageFolderStructure: remapPageFoldersTable,
|
|
158
|
+
StudioCMSPageDataTags: remapPageDataTagsTable,
|
|
159
|
+
StudioCMSPageDataCategories: remapPageDataCategoriesTable,
|
|
160
|
+
StudioCMSPluginData: remapPluginDataTable,
|
|
161
|
+
StudioCMSDynamicConfigSettings: remapDynamicConfigTable,
|
|
162
|
+
StudioCMSAPIKeys: remapApiKeysTable,
|
|
163
|
+
StudioCMSDiffTracking: remapDiffTrackingTable,
|
|
164
|
+
StudioCMSEmailVerificationTokens: remapEmailVerificationTokensTable,
|
|
165
|
+
StudioCMSOAuthAccounts: remapOAuthAccountsTable,
|
|
166
|
+
StudioCMSPageContent: remapPageContentTable,
|
|
167
|
+
StudioCMSPermissions: remapPermissionsTable,
|
|
168
|
+
StudioCMSSessionTable: remapSessionTable,
|
|
169
|
+
StudioCMSUserResetTokens: remapUserResetTokensTable,
|
|
170
|
+
};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Creates a JSON Response with the given data and status code.
|
|
3
|
+
* @param data - The data to be included in the response body.
|
|
4
|
+
* @param status - The HTTP status code (default is 200).
|
|
5
|
+
* @returns A Response object with JSON content.
|
|
6
|
+
*/
|
|
7
|
+
export const jsonResponse = (data: unknown, status = 200) => {
|
|
8
|
+
return new Response(JSON.stringify(data), {
|
|
9
|
+
status,
|
|
10
|
+
headers: { 'Content-Type': 'application/json' },
|
|
11
|
+
});
|
|
12
|
+
};
|
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
import type { Insertable } from '@withstudiocms/kysely/kysely';
|
|
2
|
+
import {
|
|
3
|
+
StudioCMSAPIKeys as KyselyStudioCMSAPIKeys,
|
|
4
|
+
StudioCMSDiffTracking as KyselyStudioCMSDiffTracking,
|
|
5
|
+
StudioCMSDynamicConfigSettings as KyselyStudioCMSDynamicConfigSettings,
|
|
6
|
+
StudioCMSEmailVerificationTokens as KyselyStudioCMSEmailVerificationTokens,
|
|
7
|
+
StudioCMSOAuthAccounts as KyselyStudioCMSOAuthAccounts,
|
|
8
|
+
StudioCMSPageContent as KyselyStudioCMSPageContent,
|
|
9
|
+
StudioCMSPageData as KyselyStudioCMSPageData,
|
|
10
|
+
StudioCMSPageDataCategories as KyselyStudioCMSPageDataCategories,
|
|
11
|
+
StudioCMSPageDataTags as KyselyStudioCMSPageDataTags,
|
|
12
|
+
StudioCMSPageFolderStructure as KyselyStudioCMSPageFolderStructure,
|
|
13
|
+
StudioCMSPermissions as KyselyStudioCMSPermissions,
|
|
14
|
+
StudioCMSPluginData as KyselyStudioCMSPluginData,
|
|
15
|
+
StudioCMSSessionTable as KyselyStudioCMSSessionTable,
|
|
16
|
+
StudioCMSUserResetTokens as KyselyStudioCMSUserResetTokens,
|
|
17
|
+
StudioCMSUsersTable as KyselyStudioCMSUsersTable,
|
|
18
|
+
} from '@withstudiocms/sdk/tables';
|
|
19
|
+
import {
|
|
20
|
+
StudioCMSAPIKeys,
|
|
21
|
+
StudioCMSDiffTracking,
|
|
22
|
+
StudioCMSDynamicConfigSettings,
|
|
23
|
+
StudioCMSEmailVerificationTokens,
|
|
24
|
+
StudioCMSOAuthAccounts,
|
|
25
|
+
StudioCMSPageContent,
|
|
26
|
+
StudioCMSPageData,
|
|
27
|
+
StudioCMSPageDataCategories,
|
|
28
|
+
StudioCMSPageDataTags,
|
|
29
|
+
StudioCMSPageFolderStructure,
|
|
30
|
+
StudioCMSPermissions,
|
|
31
|
+
StudioCMSPluginData,
|
|
32
|
+
StudioCMSSessionTable,
|
|
33
|
+
StudioCMSUserResetTokens,
|
|
34
|
+
StudioCMSUsers,
|
|
35
|
+
} from '../db/astro-db-drizzle-client.js';
|
|
36
|
+
import { getDataMigrationMeta as getAstroDataMigrationMeta } from '../db/astrodb.js';
|
|
37
|
+
import { getDataMigrationMeta as getKyselyDataMigrationMeta } from '../db/client.js';
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Schema definitions for AstroDB
|
|
41
|
+
*/
|
|
42
|
+
export const AstroDBTableSchema = {
|
|
43
|
+
StudioCMSAPIKeys,
|
|
44
|
+
StudioCMSDiffTracking,
|
|
45
|
+
StudioCMSDynamicConfigSettings,
|
|
46
|
+
StudioCMSEmailVerificationTokens,
|
|
47
|
+
StudioCMSOAuthAccounts,
|
|
48
|
+
StudioCMSPageContent,
|
|
49
|
+
StudioCMSPageData,
|
|
50
|
+
StudioCMSPageDataCategories,
|
|
51
|
+
StudioCMSPageDataTags,
|
|
52
|
+
StudioCMSPageFolderStructure,
|
|
53
|
+
StudioCMSPermissions,
|
|
54
|
+
StudioCMSPluginData,
|
|
55
|
+
StudioCMSSessionTable,
|
|
56
|
+
StudioCMSUserResetTokens,
|
|
57
|
+
StudioCMSUsers,
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Schema definitions for Kysely
|
|
62
|
+
*/
|
|
63
|
+
export const KyselyTableSchema = {
|
|
64
|
+
StudioCMSAPIKeys: KyselyStudioCMSAPIKeys,
|
|
65
|
+
StudioCMSDiffTracking: KyselyStudioCMSDiffTracking,
|
|
66
|
+
StudioCMSDynamicConfigSettings: KyselyStudioCMSDynamicConfigSettings,
|
|
67
|
+
StudioCMSEmailVerificationTokens: KyselyStudioCMSEmailVerificationTokens,
|
|
68
|
+
StudioCMSOAuthAccounts: KyselyStudioCMSOAuthAccounts,
|
|
69
|
+
StudioCMSPageContent: KyselyStudioCMSPageContent,
|
|
70
|
+
StudioCMSPageData: KyselyStudioCMSPageData,
|
|
71
|
+
StudioCMSPageDataCategories: KyselyStudioCMSPageDataCategories,
|
|
72
|
+
StudioCMSPageDataTags: KyselyStudioCMSPageDataTags,
|
|
73
|
+
StudioCMSPageFolderStructure: KyselyStudioCMSPageFolderStructure,
|
|
74
|
+
StudioCMSPermissions: KyselyStudioCMSPermissions,
|
|
75
|
+
StudioCMSPluginData: KyselyStudioCMSPluginData,
|
|
76
|
+
StudioCMSSessionTable: KyselyStudioCMSSessionTable,
|
|
77
|
+
StudioCMSUserResetTokens: KyselyStudioCMSUserResetTokens,
|
|
78
|
+
StudioCMSUsersTable: KyselyStudioCMSUsersTable,
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
// Get table keys
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Array of AstroDB table keys.
|
|
85
|
+
*/
|
|
86
|
+
export const astroDbTableKeys = Object.keys(AstroDBTableSchema) as Array<
|
|
87
|
+
keyof typeof AstroDBTableSchema
|
|
88
|
+
>;
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Array of AstroDB table keys that have no references to other tables.
|
|
92
|
+
*/
|
|
93
|
+
export const tablesWithNoReferences = [
|
|
94
|
+
'StudioCMSUsers',
|
|
95
|
+
'StudioCMSPageData',
|
|
96
|
+
'StudioCMSPageFolderStructure',
|
|
97
|
+
'StudioCMSPageDataTags',
|
|
98
|
+
'StudioCMSPageDataCategories',
|
|
99
|
+
'StudioCMSPluginData',
|
|
100
|
+
'StudioCMSDynamicConfigSettings',
|
|
101
|
+
] as const;
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Array of AstroDB table keys that have references to other tables.
|
|
105
|
+
*/
|
|
106
|
+
export const tablesWithReferences: Exclude<
|
|
107
|
+
AstroDBTableKeys,
|
|
108
|
+
(typeof tablesWithNoReferences)[number]
|
|
109
|
+
>[] = [
|
|
110
|
+
'StudioCMSAPIKeys',
|
|
111
|
+
'StudioCMSDiffTracking',
|
|
112
|
+
'StudioCMSEmailVerificationTokens',
|
|
113
|
+
'StudioCMSOAuthAccounts',
|
|
114
|
+
'StudioCMSPageContent',
|
|
115
|
+
'StudioCMSPermissions',
|
|
116
|
+
'StudioCMSSessionTable',
|
|
117
|
+
'StudioCMSUserResetTokens',
|
|
118
|
+
];
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Array of Kysely table keys.
|
|
122
|
+
*/
|
|
123
|
+
export const kyselyTableKeys = Object.keys(KyselyTableSchema) as Array<
|
|
124
|
+
keyof typeof KyselyTableSchema
|
|
125
|
+
>;
|
|
126
|
+
|
|
127
|
+
// Define table key types
|
|
128
|
+
export type AstroDBTableKeys = (typeof astroDbTableKeys)[number];
|
|
129
|
+
export type KyselyTableKeys = (typeof kyselyTableKeys)[number];
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Mapping from AstroDB table names to Kysely table names for StudioCMS.
|
|
133
|
+
*/
|
|
134
|
+
export const astroDBToKyselyMap = {
|
|
135
|
+
StudioCMSAPIKeys: 'StudioCMSAPIKeys',
|
|
136
|
+
StudioCMSDiffTracking: 'StudioCMSDiffTracking',
|
|
137
|
+
StudioCMSDynamicConfigSettings: 'StudioCMSDynamicConfigSettings',
|
|
138
|
+
StudioCMSEmailVerificationTokens: 'StudioCMSEmailVerificationTokens',
|
|
139
|
+
StudioCMSOAuthAccounts: 'StudioCMSOAuthAccounts',
|
|
140
|
+
StudioCMSPageContent: 'StudioCMSPageContent',
|
|
141
|
+
StudioCMSPageData: 'StudioCMSPageData',
|
|
142
|
+
StudioCMSPageDataCategories: 'StudioCMSPageDataCategories',
|
|
143
|
+
StudioCMSPageDataTags: 'StudioCMSPageDataTags',
|
|
144
|
+
StudioCMSPageFolderStructure: 'StudioCMSPageFolderStructure',
|
|
145
|
+
StudioCMSPermissions: 'StudioCMSPermissions',
|
|
146
|
+
StudioCMSPluginData: 'StudioCMSPluginData',
|
|
147
|
+
StudioCMSSessionTable: 'StudioCMSSessionTable',
|
|
148
|
+
StudioCMSUserResetTokens: 'StudioCMSUserResetTokens',
|
|
149
|
+
StudioCMSUsers: 'StudioCMSUsersTable',
|
|
150
|
+
} as const;
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Type representing the mapping from AstroDB table names to Kysely table names.
|
|
154
|
+
*/
|
|
155
|
+
export type AstroDBToKyselyMap = typeof astroDBToKyselyMap;
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Type representing the drizzle inferSelect type for a given AstroDB table.
|
|
159
|
+
*/
|
|
160
|
+
export type AstroDBTableType<T extends AstroDBTableKeys> =
|
|
161
|
+
(typeof AstroDBTableSchema)[T]['$inferSelect'];
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* Type representing the Kysely Insertable type for a given Kysely table.
|
|
165
|
+
*/
|
|
166
|
+
export type KyselyTableType<T extends KyselyTableKeys> = Insertable<
|
|
167
|
+
(typeof KyselyTableSchema)[T]['Encoded']
|
|
168
|
+
>;
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* Type representing the migration types for a given AstroDB table and its corresponding Kysely table.
|
|
172
|
+
*/
|
|
173
|
+
export type GetMigrationTypes<
|
|
174
|
+
AstroDB extends AstroDBTableKeys,
|
|
175
|
+
KyselyDB extends KyselyTableKeys = AstroDBToKyselyMap[AstroDB],
|
|
176
|
+
> = {
|
|
177
|
+
astro: AstroDBTableType<AstroDB>;
|
|
178
|
+
kysely: KyselyTableType<KyselyDB>;
|
|
179
|
+
};
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* Type representing a migration Key pair between an AstroDB table and its corresponding Kysely table.
|
|
183
|
+
*/
|
|
184
|
+
export type MigrationPair = {
|
|
185
|
+
astroTable: AstroDBTableKeys;
|
|
186
|
+
kyselyTable: KyselyTableKeys;
|
|
187
|
+
};
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* Get the migration key pair for a given AstroDB table key.
|
|
191
|
+
*
|
|
192
|
+
* @param key - The AstroDB table key.
|
|
193
|
+
* @returns The migration key pair.
|
|
194
|
+
*/
|
|
195
|
+
export const getMigrationPairs = <T extends AstroDBTableKeys>(key: T): MigrationPair => {
|
|
196
|
+
return {
|
|
197
|
+
astroTable: key,
|
|
198
|
+
kyselyTable: astroDBToKyselyMap[key],
|
|
199
|
+
};
|
|
200
|
+
};
|
|
201
|
+
|
|
202
|
+
// type _exampleMapping = GetMigrationTypes<'StudioCMSUsers'>;
|
|
203
|
+
// const _examplePair = getMigrationPairs('StudioCMSUsers');
|
|
204
|
+
|
|
205
|
+
/**
|
|
206
|
+
* Type representing migration metadata for both AstroDB and Kysely tables.
|
|
207
|
+
*/
|
|
208
|
+
export type MigrationMetaTables = {
|
|
209
|
+
astro: {
|
|
210
|
+
[key in AstroDBTableKeys]: number;
|
|
211
|
+
};
|
|
212
|
+
kysely: {
|
|
213
|
+
[key in KyselyTableKeys]: number;
|
|
214
|
+
};
|
|
215
|
+
};
|
|
216
|
+
|
|
217
|
+
/**
|
|
218
|
+
* Retrieve migration metadata for both AstroDB and Kysely databases.
|
|
219
|
+
*
|
|
220
|
+
* @returns An object containing migration metadata for AstroDB and Kysely tables.
|
|
221
|
+
*/
|
|
222
|
+
export const migrationMeta = async (): Promise<MigrationMetaTables> => {
|
|
223
|
+
const astro = await getAstroDataMigrationMeta().catch(() => {
|
|
224
|
+
const emptyMeta = {} as { [key in AstroDBTableKeys]: number };
|
|
225
|
+
return emptyMeta;
|
|
226
|
+
});
|
|
227
|
+
const kysely = await getKyselyDataMigrationMeta().catch(() => {
|
|
228
|
+
const emptyMeta = {} as { [key in KyselyTableKeys]: number };
|
|
229
|
+
return emptyMeta;
|
|
230
|
+
});
|
|
231
|
+
|
|
232
|
+
return {
|
|
233
|
+
astro,
|
|
234
|
+
kysely,
|
|
235
|
+
};
|
|
236
|
+
};
|
|
@@ -0,0 +1,268 @@
|
|
|
1
|
+
import type { APIRoute } from 'astro';
|
|
2
|
+
import { db } from '../db/astro-db-drizzle-client.js';
|
|
3
|
+
import { getStudioCMSDb } from '../db/client.js';
|
|
4
|
+
import logger from '../lib/logger.js';
|
|
5
|
+
import { remapFunctions } from '../lib/remapUtils.js';
|
|
6
|
+
import { jsonResponse } from '../lib/response-utils.js';
|
|
7
|
+
import { AstroDBTableSchema } from '../lib/tableMap.js';
|
|
8
|
+
|
|
9
|
+
const studioCMSDb = await getStudioCMSDb();
|
|
10
|
+
|
|
11
|
+
async function migrateUsersTable() {
|
|
12
|
+
const astroUsers = await db.select().from(AstroDBTableSchema.StudioCMSUsers);
|
|
13
|
+
|
|
14
|
+
if (astroUsers.length > 0) {
|
|
15
|
+
const remappedData = remapFunctions.StudioCMSUsers(astroUsers);
|
|
16
|
+
|
|
17
|
+
const insertResult = await studioCMSDb.db
|
|
18
|
+
.insertInto('StudioCMSUsersTable')
|
|
19
|
+
.values(remappedData)
|
|
20
|
+
.executeTakeFirst();
|
|
21
|
+
|
|
22
|
+
logger.info(`Migrated ${insertResult.numInsertedOrUpdatedRows} users.`);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
async function migratePageDataTable() {
|
|
27
|
+
const astroPageData = await db.select().from(AstroDBTableSchema.StudioCMSPageData);
|
|
28
|
+
|
|
29
|
+
if (astroPageData.length > 0) {
|
|
30
|
+
const remappedData = remapFunctions.StudioCMSPageData(astroPageData);
|
|
31
|
+
|
|
32
|
+
const insertResult = await studioCMSDb.db
|
|
33
|
+
.insertInto('StudioCMSPageData')
|
|
34
|
+
.values(remappedData)
|
|
35
|
+
.executeTakeFirst();
|
|
36
|
+
|
|
37
|
+
logger.info(`Migrated ${insertResult.numInsertedOrUpdatedRows} page data items.`);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
async function migratePageFolderStructureTable() {
|
|
42
|
+
const astroPageFolders = await db.select().from(AstroDBTableSchema.StudioCMSPageFolderStructure);
|
|
43
|
+
|
|
44
|
+
if (astroPageFolders.length > 0) {
|
|
45
|
+
const remappedData = remapFunctions.StudioCMSPageFolderStructure(astroPageFolders);
|
|
46
|
+
|
|
47
|
+
const insertResult = await studioCMSDb.db
|
|
48
|
+
.insertInto('StudioCMSPageFolderStructure')
|
|
49
|
+
.values(remappedData)
|
|
50
|
+
.executeTakeFirst();
|
|
51
|
+
|
|
52
|
+
logger.info(`Migrated ${insertResult.numInsertedOrUpdatedRows} page folder items.`);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
async function migratePageDataTagsTable() {
|
|
57
|
+
const astroPageDataTags = await db.select().from(AstroDBTableSchema.StudioCMSPageDataTags);
|
|
58
|
+
|
|
59
|
+
if (astroPageDataTags.length > 0) {
|
|
60
|
+
const remappedData = remapFunctions.StudioCMSPageDataTags(astroPageDataTags);
|
|
61
|
+
|
|
62
|
+
const insertResult = await studioCMSDb.db
|
|
63
|
+
.insertInto('StudioCMSPageDataTags')
|
|
64
|
+
.values(remappedData)
|
|
65
|
+
.executeTakeFirst();
|
|
66
|
+
|
|
67
|
+
logger.info(`Migrated ${insertResult.numInsertedOrUpdatedRows} page data tags.`);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
async function migratePageDataCategoriesTable() {
|
|
72
|
+
const astroPageDataCategories = await db
|
|
73
|
+
.select()
|
|
74
|
+
.from(AstroDBTableSchema.StudioCMSPageDataCategories);
|
|
75
|
+
|
|
76
|
+
if (astroPageDataCategories.length > 0) {
|
|
77
|
+
const remappedData = remapFunctions.StudioCMSPageDataCategories(astroPageDataCategories);
|
|
78
|
+
|
|
79
|
+
const insertResult = await studioCMSDb.db
|
|
80
|
+
.insertInto('StudioCMSPageDataCategories')
|
|
81
|
+
.values(remappedData)
|
|
82
|
+
.executeTakeFirst();
|
|
83
|
+
|
|
84
|
+
logger.info(`Migrated ${insertResult.numInsertedOrUpdatedRows} page data categories.`);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
async function migratePluginDataTable() {
|
|
89
|
+
const astroPluginData = await db.select().from(AstroDBTableSchema.StudioCMSPluginData);
|
|
90
|
+
|
|
91
|
+
if (astroPluginData.length > 0) {
|
|
92
|
+
const remappedData = remapFunctions.StudioCMSPluginData(astroPluginData);
|
|
93
|
+
|
|
94
|
+
const insertResult = await studioCMSDb.db
|
|
95
|
+
.insertInto('StudioCMSPluginData')
|
|
96
|
+
.values(remappedData)
|
|
97
|
+
.executeTakeFirst();
|
|
98
|
+
|
|
99
|
+
logger.info(`Migrated ${insertResult.numInsertedOrUpdatedRows} plugin data items.`);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
async function migrateDynamicConfigSettingsTable() {
|
|
104
|
+
const astroConfigSettings = await db
|
|
105
|
+
.select()
|
|
106
|
+
.from(AstroDBTableSchema.StudioCMSDynamicConfigSettings);
|
|
107
|
+
|
|
108
|
+
if (astroConfigSettings.length > 0) {
|
|
109
|
+
const remappedData = remapFunctions.StudioCMSDynamicConfigSettings(astroConfigSettings);
|
|
110
|
+
|
|
111
|
+
const insertResult = await studioCMSDb.db
|
|
112
|
+
.insertInto('StudioCMSDynamicConfigSettings')
|
|
113
|
+
.values(remappedData)
|
|
114
|
+
.executeTakeFirst();
|
|
115
|
+
|
|
116
|
+
logger.info(`Migrated ${insertResult.numInsertedOrUpdatedRows} dynamic config settings.`);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
async function migrateApiKeysTable() {
|
|
121
|
+
const astroApiKeys = await db.select().from(AstroDBTableSchema.StudioCMSAPIKeys);
|
|
122
|
+
|
|
123
|
+
if (astroApiKeys.length > 0) {
|
|
124
|
+
const remappedData = remapFunctions.StudioCMSAPIKeys(astroApiKeys);
|
|
125
|
+
|
|
126
|
+
const insertResult = await studioCMSDb.db
|
|
127
|
+
.insertInto('StudioCMSAPIKeys')
|
|
128
|
+
.values(remappedData)
|
|
129
|
+
.executeTakeFirst();
|
|
130
|
+
|
|
131
|
+
logger.info(`Migrated ${insertResult.numInsertedOrUpdatedRows} API keys.`);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
async function migrateUserResetTokensTable() {
|
|
136
|
+
const astroResetTokens = await db.select().from(AstroDBTableSchema.StudioCMSUserResetTokens);
|
|
137
|
+
|
|
138
|
+
if (astroResetTokens.length > 0) {
|
|
139
|
+
const remappedData = remapFunctions.StudioCMSUserResetTokens(astroResetTokens);
|
|
140
|
+
|
|
141
|
+
const insertResult = await studioCMSDb.db
|
|
142
|
+
.insertInto('StudioCMSUserResetTokens')
|
|
143
|
+
.values(remappedData)
|
|
144
|
+
.executeTakeFirst();
|
|
145
|
+
|
|
146
|
+
logger.info(`Migrated ${insertResult.numInsertedOrUpdatedRows} user reset tokens.`);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
async function migrateOAuthAccountsTable() {
|
|
151
|
+
const astroOAuthAccounts = await db.select().from(AstroDBTableSchema.StudioCMSOAuthAccounts);
|
|
152
|
+
|
|
153
|
+
if (astroOAuthAccounts.length > 0) {
|
|
154
|
+
const remappedData = remapFunctions.StudioCMSOAuthAccounts(astroOAuthAccounts);
|
|
155
|
+
|
|
156
|
+
const insertResult = await studioCMSDb.db
|
|
157
|
+
.insertInto('StudioCMSOAuthAccounts')
|
|
158
|
+
.values(remappedData)
|
|
159
|
+
.executeTakeFirst();
|
|
160
|
+
|
|
161
|
+
logger.info(`Migrated ${insertResult.numInsertedOrUpdatedRows} OAuth accounts.`);
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
async function migrateSessionTable() {
|
|
166
|
+
const astroSessions = await db.select().from(AstroDBTableSchema.StudioCMSSessionTable);
|
|
167
|
+
|
|
168
|
+
if (astroSessions.length > 0) {
|
|
169
|
+
const remappedData = remapFunctions.StudioCMSSessionTable(astroSessions);
|
|
170
|
+
|
|
171
|
+
const insertResult = await studioCMSDb.db
|
|
172
|
+
.insertInto('StudioCMSSessionTable')
|
|
173
|
+
.values(remappedData)
|
|
174
|
+
.executeTakeFirst();
|
|
175
|
+
|
|
176
|
+
logger.info(`Migrated ${insertResult.numInsertedOrUpdatedRows} sessions.`);
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
async function migratePermissionsTable() {
|
|
181
|
+
const astroPermissions = await db.select().from(AstroDBTableSchema.StudioCMSPermissions);
|
|
182
|
+
|
|
183
|
+
if (astroPermissions.length > 0) {
|
|
184
|
+
const remappedData = remapFunctions.StudioCMSPermissions(astroPermissions);
|
|
185
|
+
|
|
186
|
+
const insertResult = await studioCMSDb.db
|
|
187
|
+
.insertInto('StudioCMSPermissions')
|
|
188
|
+
.values(remappedData)
|
|
189
|
+
.executeTakeFirst();
|
|
190
|
+
|
|
191
|
+
logger.info(`Migrated ${insertResult.numInsertedOrUpdatedRows} permissions.`);
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
async function migrateDiffTrackingTable() {
|
|
196
|
+
const astroDiffTracking = await db.select().from(AstroDBTableSchema.StudioCMSDiffTracking);
|
|
197
|
+
|
|
198
|
+
if (astroDiffTracking.length > 0) {
|
|
199
|
+
const remappedData = remapFunctions.StudioCMSDiffTracking(astroDiffTracking);
|
|
200
|
+
|
|
201
|
+
const insertResult = await studioCMSDb.db
|
|
202
|
+
.insertInto('StudioCMSDiffTracking')
|
|
203
|
+
.values(remappedData)
|
|
204
|
+
.executeTakeFirst();
|
|
205
|
+
|
|
206
|
+
logger.info(`Migrated ${insertResult.numInsertedOrUpdatedRows} diff tracking items.`);
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
async function migratePageContentTable() {
|
|
211
|
+
const astroPageContent = await db.select().from(AstroDBTableSchema.StudioCMSPageContent);
|
|
212
|
+
|
|
213
|
+
if (astroPageContent.length > 0) {
|
|
214
|
+
const remappedData = remapFunctions.StudioCMSPageContent(astroPageContent);
|
|
215
|
+
|
|
216
|
+
const insertResult = await studioCMSDb.db
|
|
217
|
+
.insertInto('StudioCMSPageContent')
|
|
218
|
+
.values(remappedData)
|
|
219
|
+
.executeTakeFirst();
|
|
220
|
+
|
|
221
|
+
logger.info(`Migrated ${insertResult.numInsertedOrUpdatedRows} page content items.`);
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
async function migrateEmailVerificationTokensTable() {
|
|
226
|
+
const astroEmailTokens = await db
|
|
227
|
+
.select()
|
|
228
|
+
.from(AstroDBTableSchema.StudioCMSEmailVerificationTokens);
|
|
229
|
+
|
|
230
|
+
if (astroEmailTokens.length > 0) {
|
|
231
|
+
const remappedData = remapFunctions.StudioCMSEmailVerificationTokens(astroEmailTokens);
|
|
232
|
+
|
|
233
|
+
const insertResult = await studioCMSDb.db
|
|
234
|
+
.insertInto('StudioCMSEmailVerificationTokens')
|
|
235
|
+
.values(remappedData)
|
|
236
|
+
.executeTakeFirst();
|
|
237
|
+
|
|
238
|
+
logger.info(`Migrated ${insertResult.numInsertedOrUpdatedRows} email verification tokens.`);
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
export const POST: APIRoute = async () => {
|
|
243
|
+
try {
|
|
244
|
+
// Migrate Tables Without References First
|
|
245
|
+
await migrateUsersTable();
|
|
246
|
+
await migratePageDataTable();
|
|
247
|
+
await migratePageFolderStructureTable();
|
|
248
|
+
await migratePageDataTagsTable();
|
|
249
|
+
await migratePageDataCategoriesTable();
|
|
250
|
+
await migratePluginDataTable();
|
|
251
|
+
await migrateDynamicConfigSettingsTable();
|
|
252
|
+
|
|
253
|
+
// Then migrate tables with references
|
|
254
|
+
await migrateApiKeysTable();
|
|
255
|
+
await migrateUserResetTokensTable();
|
|
256
|
+
await migrateOAuthAccountsTable();
|
|
257
|
+
await migrateSessionTable();
|
|
258
|
+
await migratePermissionsTable();
|
|
259
|
+
await migrateDiffTrackingTable();
|
|
260
|
+
await migratePageContentTable();
|
|
261
|
+
await migrateEmailVerificationTokensTable();
|
|
262
|
+
} catch (error) {
|
|
263
|
+
logger.error(`Migration failed: ${(error as Error).message}`);
|
|
264
|
+
return jsonResponse({ success: false, error: 'Migration failed' }, 500);
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
return jsonResponse({ success: true });
|
|
268
|
+
};
|