-
Notifications
You must be signed in to change notification settings - Fork 11
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
chore: add database migration script for latest username update.
- Loading branch information
Showing
8 changed files
with
244 additions
and
111 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
152 changes: 152 additions & 0 deletions
152
src/routes/(internal)/__internal__/admin/migration-508f757/+page.server.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,152 @@ | ||
import { claimUsername, unsetUsername, listUsers, userSubspaceByRauthyId } from '$lib/usernames'; | ||
import { fail, type ServerLoad } from '@sveltejs/kit'; | ||
import type { Actions } from './$types'; | ||
import { | ||
DatabaseDumpSchema, | ||
base32Decode, | ||
borshDeserialize, | ||
type DatabaseDump, | ||
type SubspaceId, | ||
type DatabaseDumpDocument, | ||
type DatabaseDumpSubspace, | ||
formatEntityPath, | ||
type ExactLink, | ||
Component, | ||
BorshSchema | ||
} from 'leaf-proto'; | ||
import { WEIRD_NAMESPACE, leafClient, subspace_link } from '$lib/leaf'; | ||
import _ from 'underscore'; | ||
import { CommonMark, RawImage } from 'leaf-proto/components'; | ||
import { WeirdCustomDomain, setAvatar } from '$lib/leaf/profile'; | ||
|
||
import { createAvatar } from '@dicebear/core'; | ||
import { glass } from '@dicebear/collection'; | ||
|
||
export const load: ServerLoad = async ({}) => { | ||
const users = []; | ||
for await (const user of listUsers()) users.push(user); | ||
return { users }; | ||
}; | ||
|
||
class Username extends Component { | ||
value: string = ''; | ||
constructor(s: string) { | ||
super(); | ||
this.value = s; | ||
} | ||
static componentName(): string { | ||
return 'Username'; | ||
} | ||
static borshSchema(): BorshSchema { | ||
return BorshSchema.String; | ||
} | ||
static specification(): Component[] { | ||
return [new CommonMark('The username of the user represented by this entity.')]; | ||
} | ||
} | ||
|
||
export const actions = { | ||
migrate: async ({ request }) => { | ||
const formData = await request.formData(); | ||
const subspaceIdStr = formData.get('subspaceId'); | ||
if (!subspaceIdStr) | ||
return fail(400, { | ||
error: 'You must provide the weird instance subspace ID to import from.' | ||
}); | ||
let subspaceId: SubspaceId; | ||
try { | ||
subspaceId = base32Decode(subspaceIdStr.toString()); | ||
} catch (_) { | ||
return fail(400, { error: 'Could not parse subspace ID.' }); | ||
} | ||
const dumpFormData = formData.get('dump'); | ||
if (!dumpFormData) return fail(400, { error: 'You must provide database dump file.' }); | ||
const dumpData = new Uint8Array(await (dumpFormData as File).arrayBuffer()); | ||
const dump: DatabaseDump = borshDeserialize(DatabaseDumpSchema, dumpData); | ||
|
||
let doc: DatabaseDumpDocument | undefined; | ||
if (dump.documents.size != 1) return fail(400, { error: 'Dump has multiple namespaces' }); | ||
for (const [namespace, document] of dump.documents) { | ||
const n = new Uint8Array(namespace); | ||
if (_.isEqual(n, WEIRD_NAMESPACE)) { | ||
doc = document; | ||
break; | ||
} | ||
} | ||
if (doc === undefined) { | ||
return fail(400, { error: 'Dump is missing weird namespace' }); | ||
} | ||
|
||
let subspace: DatabaseDumpSubspace | undefined; | ||
for (const [id, ss] of doc.subspaces) { | ||
const i = new Uint8Array(id); | ||
if (_.isEqual(subspaceId, i)) { | ||
subspace = ss; | ||
break; | ||
} | ||
} | ||
if (subspace === undefined) { | ||
return fail(400, { error: 'could not find specified subspace in dump' }); | ||
} | ||
|
||
for (const [path, entity] of subspace) { | ||
if (_.isEqual(path[0], { String: 'profiles' })) { | ||
if ('String' in path[1]) { | ||
const rauthyId = path[1].String; | ||
const subspace = await userSubspaceByRauthyId(rauthyId); | ||
|
||
// User profile | ||
let newLink: ExactLink; | ||
const isProfile = path.length == 2; | ||
if (isProfile) { | ||
newLink = subspace_link(subspace, null); | ||
} else { | ||
newLink = subspace_link(subspace, ...path.slice(2)); | ||
} | ||
|
||
const components = []; | ||
let username: string | undefined; | ||
let customDomain: string | undefined; | ||
let hasAvatar = false; | ||
for (const [schema, componentDatas] of entity.components) { | ||
const s = new Uint8Array(schema); | ||
if (_.isEqual(Username.schemaId(), s)) { | ||
username = Username.deserialize(new Uint8Array(componentDatas[0])) as any; | ||
username = username?.split('@')[0].toLowerCase().replace('.', '-'); | ||
continue; | ||
} | ||
if (_.isEqual(WeirdCustomDomain.schemaId(), s)) { | ||
customDomain = WeirdCustomDomain.deserialize( | ||
new Uint8Array(componentDatas[0]) | ||
) as any; | ||
continue; | ||
} | ||
if (_.isEqual(s, RawImage.schemaId())) { | ||
hasAvatar = true; | ||
} | ||
for (const data of componentDatas) { | ||
console; | ||
components.push({ schema: new Uint8Array(schema), data: new Uint8Array(data) }); | ||
} | ||
} | ||
if (customDomain) { | ||
await claimUsername({ domain: customDomain, skipDomainCheck: true }, rauthyId); | ||
} else if (username) { | ||
await claimUsername({ username }, rauthyId); | ||
} | ||
await leafClient.add_components(newLink, components); | ||
|
||
if (isProfile && !hasAvatar) { | ||
const avatar = createAvatar(glass, { seed: rauthyId, radius: 50 }); | ||
await setAvatar( | ||
newLink, | ||
new RawImage('image/svg+xml', new TextEncoder().encode(avatar.toString())) | ||
); | ||
} | ||
} | ||
} | ||
} | ||
|
||
return { dump: `${JSON.stringify(dump, null, ' ')}` }; | ||
} | ||
} satisfies Actions; |
63 changes: 63 additions & 0 deletions
63
src/routes/(internal)/__internal__/admin/migration-508f757/+page.svelte
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
<script lang="ts"> | ||
import { base32Encode } from 'leaf-proto'; | ||
import type { ActionData, PageData } from './$types'; | ||
const { form, data }: { form: ActionData; data: PageData } = $props(); | ||
</script> | ||
|
||
{#if form?.error} | ||
<article class="pico-background-red-550"> | ||
{form.error} | ||
</article> | ||
{:else if form?.dump} | ||
<article> | ||
<pre> | ||
|
||
{form.dump} | ||
</pre> | ||
</article> | ||
{/if} | ||
|
||
<h2>Migrate Database</h2> | ||
|
||
<p> | ||
Upload a dump of the previous version of the database and it will be imported and migrated to the | ||
new version. | ||
</p> | ||
|
||
<p> | ||
Below you can see the commits for the previous version of Weird that we are migrating from, and | ||
the commit after that which includes the newer data model that we are migrating to. | ||
</p> | ||
|
||
<table> | ||
<thead> | ||
<tr> | ||
<td>Previous Version Commit</td> | ||
<td>Current Version Commit</td> | ||
</tr> | ||
</thead> | ||
<tbody> | ||
<tr> | ||
<td> | ||
<a href="https://github.com/muni-town/weird/commit/e291612d36be303eecbe136967c39fa5e2b6af5a" | ||
>e291612d36be303eecbe136967c39fa5e2b6af5a</a | ||
> | ||
</td> | ||
<td> | ||
<a | ||
href="https://github.com/muni-town/weird/commit/508f757686d3ca20fa5b8b4a29b09c2fe992b4a3" | ||
> | ||
508f757686d3ca20fa5b8b4a29b09c2fe992b4a3 | ||
</a> | ||
</td> | ||
</tr> | ||
</tbody> | ||
</table> | ||
|
||
<form method="post" action="?/migrate" enctype="multipart/form-data"> | ||
<!-- svelte-ignore a11y_no_redundant_roles --> | ||
<input name="subspaceId" placeholder="subspaceId" /> | ||
<input type="file" name="dump" accept=".bin" /> | ||
<button>Migrate</button> | ||
</form> |
49 changes: 0 additions & 49 deletions
49
src/routes/(internal)/__internal__/admin/migration/+page.server.ts
This file was deleted.
Oops, something went wrong.
Oops, something went wrong.