sdadsfsdfdsfa
This commit is contained in:
parent
6abbd6b2c0
commit
8dae161d7c
10 changed files with 338 additions and 43 deletions
|
@ -44,6 +44,7 @@
|
|||
"dependencies": {
|
||||
"@auth/core": "^0.5.0",
|
||||
"@auth/sveltekit": "^0.2.2",
|
||||
"@ltd/j-toml": "^1.38.0",
|
||||
"@rgossiaux/svelte-headlessui": "^1.0.2",
|
||||
"@xmcl/modrinth": "^1.1.0",
|
||||
"minio": "^7.0.32",
|
||||
|
@ -51,7 +52,6 @@
|
|||
"nanoid": "^4.0.1",
|
||||
"octokit": "^2.0.14",
|
||||
"svelte-feather-icons": "^4.0.0",
|
||||
"toml": "^3.0.0",
|
||||
"zod": "^3.20.6"
|
||||
}
|
||||
}
|
||||
|
|
16
src/lib/clients.ts
Normal file
16
src/lib/clients.ts
Normal file
|
@ -0,0 +1,16 @@
|
|||
import Nano from 'nano';
|
||||
import Minio from 'minio';
|
||||
import {
|
||||
COUCHDB_URL,
|
||||
S3_ACCESS_KEY_ID,
|
||||
S3_ENDPOINT,
|
||||
S3_SECRET_ACCESS_KEY
|
||||
} from '$env/static/private';
|
||||
|
||||
export const nano = Nano(COUCHDB_URL);
|
||||
export const minioClient = new Minio.Client({
|
||||
endPoint: S3_ENDPOINT,
|
||||
useSSL: true,
|
||||
accessKey: S3_ACCESS_KEY_ID,
|
||||
secretKey: S3_SECRET_ACCESS_KEY
|
||||
});
|
145
src/lib/packwiz-types.ts
Normal file
145
src/lib/packwiz-types.ts
Normal file
|
@ -0,0 +1,145 @@
|
|||
import { z } from 'zod';
|
||||
|
||||
export const packSchema = z.object({
|
||||
index: z.object({
|
||||
file: z.string(),
|
||||
'hash-format': z.enum(['sha256', 'sha512', 'sha1', 'md5', 'murmur2']),
|
||||
hash: z.string().regex(/^[a-fA-F0-9]+$/)
|
||||
}),
|
||||
name: z.string(),
|
||||
'pack-format': z.string().default('packwiz:1.0.0'),
|
||||
versions: z.object({
|
||||
minecraft: z.string(),
|
||||
fabric: z.string().optional(),
|
||||
forge: z.string().optional(),
|
||||
liteloader: z.string().optional(),
|
||||
quilt: z.string().optional()
|
||||
}),
|
||||
author: z.string().optional(),
|
||||
description: z.string().optional(),
|
||||
version: z.string().optional()
|
||||
});
|
||||
|
||||
export const indexSchema = z.object({
|
||||
'hash-format': z.enum(['sha256', 'sha512', 'sha1', 'md5', 'murmur2']),
|
||||
files: z
|
||||
.object({
|
||||
file: z.string(),
|
||||
hash: z.string().regex(/^[a-fA-F0-9]+$/),
|
||||
alias: z.string(),
|
||||
'hash-format': z.enum(['sha256', 'sha512', 'sha1', 'md5', 'murmur2']),
|
||||
metafile: z.boolean().default(false),
|
||||
preserve: z.boolean().default(false)
|
||||
})
|
||||
.array(),
|
||||
update: z
|
||||
.object({
|
||||
curseforge: z
|
||||
.object({
|
||||
'file-id': z.number(),
|
||||
'project-id': z.number()
|
||||
})
|
||||
.optional(),
|
||||
modrinth: z
|
||||
.object({
|
||||
'mod-id': z.string(),
|
||||
version: z.string()
|
||||
})
|
||||
.optional()
|
||||
})
|
||||
.default({})
|
||||
});
|
||||
|
||||
// export const metafileSchema = z.object({
|
||||
// download: z.object({
|
||||
// 'hash-format': z.enum(['sha256', 'sha512', 'sha1', 'md5', 'murmur2']),
|
||||
// hash: z.string().regex(/^[a-fA-F0-9]+$/),
|
||||
// url: z.string().url().optional(),
|
||||
// mode: z.enum(['url', 'metadata:curseforge']).default('url')
|
||||
// }),
|
||||
// filename: z.string(),
|
||||
// name: z.string(),
|
||||
// option: z
|
||||
// .object({
|
||||
// optional: z.boolean(),
|
||||
// default: z.boolean().default(false),
|
||||
// description: z.string().optional()
|
||||
// })
|
||||
// .default({ optional: false }),
|
||||
// side: z.enum(['server', 'client', 'both']).default('both'),
|
||||
// update: z
|
||||
// .object({
|
||||
// curseforge: z
|
||||
// .object({
|
||||
// 'file-id': z.bigint(),
|
||||
// 'project-id': z.bigint()
|
||||
// })
|
||||
// .optional(),
|
||||
// modrinth: z
|
||||
// .object({
|
||||
// 'mod-id': z.string(),
|
||||
// version: z.string()
|
||||
// })
|
||||
// .optional()
|
||||
// })
|
||||
// .default({})
|
||||
// });
|
||||
|
||||
const metafileBase = z.object({
|
||||
filename: z.string(),
|
||||
name: z.string(),
|
||||
option: z
|
||||
.object({
|
||||
optional: z.boolean(),
|
||||
default: z.boolean().default(false),
|
||||
description: z.string().optional()
|
||||
})
|
||||
.default({ optional: false }),
|
||||
side: z.enum(['server', 'client', 'both']).default('both')
|
||||
});
|
||||
|
||||
export const metafileSchema = z.union([
|
||||
metafileBase.extend({
|
||||
download: z.object({
|
||||
'hash-format': z.enum(['sha256', 'sha512', 'sha1', 'md5', 'murmur2']),
|
||||
hash: z.string().regex(/^[a-fA-F0-9]+$/),
|
||||
url: z.string().url(),
|
||||
mode: z.literal('url').default('url')
|
||||
}),
|
||||
update: z
|
||||
.object({
|
||||
curseforge: z
|
||||
.object({
|
||||
'file-id': z.bigint(),
|
||||
'project-id': z.bigint()
|
||||
})
|
||||
.optional(),
|
||||
modrinth: z
|
||||
.object({
|
||||
'mod-id': z.string(),
|
||||
version: z.string()
|
||||
})
|
||||
.optional()
|
||||
})
|
||||
.default({})
|
||||
}),
|
||||
metafileBase.extend({
|
||||
download: z.object({
|
||||
'hash-format': z.enum(['sha256', 'sha512', 'sha1', 'md5', 'murmur2']),
|
||||
hash: z.string().regex(/^[a-fA-F0-9]+$/),
|
||||
mode: z.literal('metadata:curseforge')
|
||||
}),
|
||||
update: z.object({
|
||||
curseforge: z.object({
|
||||
'file-id': z.bigint(),
|
||||
'project-id': z.bigint()
|
||||
}),
|
||||
modrinth: z
|
||||
.object({
|
||||
'mod-id': z.string(),
|
||||
version: z.string()
|
||||
})
|
||||
.optional()
|
||||
})
|
||||
})
|
||||
]);
|
|
@ -10,15 +10,15 @@ export const fileInlineSchema = z.object({
|
|||
inline: z.literal(true),
|
||||
metafile: z.boolean(),
|
||||
content: z.string().max(256 * 1024),
|
||||
sha1: z.string().regex(/^[a-fA-F0-9]{40}$/),
|
||||
sha512: z.string().regex(/^[a-fA-F0-9]{128}$/)
|
||||
sha1: z.string().regex(/^[a-f0-9]{40}$/),
|
||||
sha512: z.string().regex(/^[a-f0-9]{128}$/)
|
||||
});
|
||||
|
||||
export const fileBlobSchema = z.object({
|
||||
type: z.literal('file'),
|
||||
inline: z.literal(false),
|
||||
sha1: z.string().regex(/^[a-fA-F0-9]{40}$/),
|
||||
sha512: z.string().regex(/^[a-fA-F0-9]{128}$/)
|
||||
sha1: z.string().regex(/^[a-f0-9]{40}$/),
|
||||
sha512: z.string().regex(/^[a-f0-9]{128}$/)
|
||||
});
|
||||
|
||||
export const fileSchema = z.union([fileInlineSchema, fileBlobSchema]);
|
||||
|
|
|
@ -1 +1,2 @@
|
|||
Front Page Here
|
||||
<a href="/create">create</a>
|
||||
|
|
|
@ -1,33 +1,16 @@
|
|||
import type { Actions } from './$types';
|
||||
import Nano from 'nano';
|
||||
import Minio from 'minio';
|
||||
import {
|
||||
COUCHDB_URL,
|
||||
COUCHDB_DB_NAME,
|
||||
S3_ACCESS_KEY_ID,
|
||||
S3_BUCKET_NAME,
|
||||
S3_ENDPOINT,
|
||||
S3_SECRET_ACCESS_KEY
|
||||
} from '$env/static/private';
|
||||
import { COUCHDB_DB_NAME, S3_BUCKET_NAME } from '$env/static/private';
|
||||
import { fail, redirect } from '@sveltejs/kit';
|
||||
import { fileTreeSchema } from '$lib/types-zod';
|
||||
import { get_blobs } from '$lib/utils';
|
||||
import type { Modpack } from '$lib/types';
|
||||
import { nanoid } from 'nanoid';
|
||||
|
||||
const nano = Nano(COUCHDB_URL);
|
||||
|
||||
const minioClient = new Minio.Client({
|
||||
endPoint: S3_ENDPOINT,
|
||||
useSSL: true,
|
||||
accessKey: S3_ACCESS_KEY_ID,
|
||||
secretKey: S3_SECRET_ACCESS_KEY
|
||||
});
|
||||
import { minioClient, nano } from '$lib/clients';
|
||||
|
||||
export const actions: Actions = {
|
||||
default: async (event) => {
|
||||
const session = await event.locals.getSession();
|
||||
if (!session?.user) throw redirect(303, '/auth');
|
||||
if (!session?.user) throw redirect(303, '/auth/signin');
|
||||
const data = await event.request.formData();
|
||||
const payload = data.get('payload');
|
||||
const title = data.get('title');
|
||||
|
@ -45,7 +28,10 @@ export const actions: Actions = {
|
|||
const buf = await file.arrayBuffer();
|
||||
const hashBuffer = await crypto.subtle.digest('SHA-512', buf);
|
||||
const hashArray = Array.from(new Uint8Array(hashBuffer));
|
||||
const hash = hashArray.map((b) => b.toString(16).padStart(2, '0')).join('');
|
||||
const hash = hashArray
|
||||
.map((b) => b.toString(16).padStart(2, '0'))
|
||||
.join('')
|
||||
.toLowerCase();
|
||||
if (hash != blob.sha512) throw fail(400);
|
||||
let exists = false;
|
||||
try {
|
||||
|
@ -63,12 +49,14 @@ export const actions: Actions = {
|
|||
_id: nanoid(),
|
||||
name: title,
|
||||
author: session.user.email ?? 'unknown@nodomain',
|
||||
revisions: [{
|
||||
revisions: [
|
||||
{
|
||||
id: nanoid(),
|
||||
type: 'base',
|
||||
tree: payload_decoded.data,
|
||||
message: 'initial revision'
|
||||
}]
|
||||
}
|
||||
]
|
||||
};
|
||||
await nano.use(COUCHDB_DB_NAME).insert(document);
|
||||
}
|
||||
|
|
|
@ -1,7 +1,103 @@
|
|||
<script lang="ts">
|
||||
import type { PageData } from './$types';
|
||||
import FlattenedFileList from './FlattenedFileList.svelte';
|
||||
|
||||
export let data: PageData;
|
||||
import type { Folder } from '$lib/types';
|
||||
import {
|
||||
Tab,
|
||||
TabGroup,
|
||||
TabList,
|
||||
TabPanel,
|
||||
TabPanels,
|
||||
Dialog,
|
||||
DialogOverlay,
|
||||
DialogTitle,
|
||||
DialogDescription
|
||||
} from '@rgossiaux/svelte-headlessui';
|
||||
import { PlusIcon } from 'svelte-feather-icons';
|
||||
let fileTree: Folder = {
|
||||
type: 'folder',
|
||||
children: {
|
||||
mods: { type: 'folder', children: {
|
||||
"quark.pw.toml": {
|
||||
type: 'file',
|
||||
inline: true,
|
||||
metafile: true,
|
||||
content: `name = "Quark"
|
||||
filename = "Quark-3.4-389.jar"
|
||||
side = "both"
|
||||
|
||||
[download]
|
||||
hash-format = "sha1"
|
||||
hash = "9a2fcfae363db7330abc7a658bc7e016ae72fb5e"
|
||||
mode = "metadata:curseforge"
|
||||
|
||||
[update]
|
||||
[update.curseforge]
|
||||
file-id = 4366541
|
||||
project-id = 243121
|
||||
`,
|
||||
sha1: '478805ce54b082dadb1042ccfb46a68cfde3fc35',
|
||||
sha512: '5d364049ff53af2154ccae58dd53d2111ef592b21bff03705314cd6d12378cfeb6376b8bc3065ac59e1c9452447bec0eb345ecc19f0de1a17c20ea374f802f05'
|
||||
},
|
||||
"waystones.pw.toml": {
|
||||
type: 'file',
|
||||
inline: true,
|
||||
metafile: true,
|
||||
content: `name = "Waystones"
|
||||
filename = "waystones-forge-1.19-11.1.0.jar"
|
||||
side = "both"
|
||||
|
||||
[download]
|
||||
url = "https://cdn.modrinth.com/data/LOpKHB2A/versions/11.1.0%2Bforge-1.19/waystones-forge-1.19-11.1.0.jar"
|
||||
hash-format = "sha1"
|
||||
hash = "24c5403c1d5791f977a0ba69f08cf7959169c685"
|
||||
|
||||
[update]
|
||||
[update.modrinth]
|
||||
mod-id = "LOpKHB2A"
|
||||
version = "2sIhirkG"
|
||||
`,
|
||||
sha1: '25f4b57fbd8a985b4872bb8fdadd14580b0f9f81',
|
||||
sha512: '125fb6fc706363753c1c201192024415a23c1098c8e44d5deeaf5448215d83108b9a9a5aba0a029dd9015eff536dc1da63d16c3be0d7f9e39d6f482b8ce008c3'
|
||||
}
|
||||
} }
|
||||
}
|
||||
};
|
||||
let blobs: Blob[] = [];
|
||||
let isOpen = false;
|
||||
</script>
|
||||
|
||||
{JSON.stringify(data.session.user)}
|
||||
<TabGroup>
|
||||
<TabList>
|
||||
<Tab>Simple</Tab>
|
||||
<Tab>Advanced</Tab>
|
||||
</TabList>
|
||||
<TabPanels>
|
||||
<TabPanel>
|
||||
<h1>Mods <button><PlusIcon /></button></h1>
|
||||
<FlattenedFileList tree={fileTree.children.mods ?? { type: 'folder', children: {} }} />
|
||||
<h1>Resource Packs <button><PlusIcon /></button></h1>
|
||||
<FlattenedFileList
|
||||
tree={fileTree.children.resourcepacks ?? { type: 'folder', children: {} }}
|
||||
/>
|
||||
<h1>Shader Packs <button><PlusIcon /></button></h1>
|
||||
<FlattenedFileList tree={fileTree.children.shaderpacks ?? { type: 'folder', children: {} }} />
|
||||
</TabPanel>
|
||||
<TabPanel>Content 2</TabPanel>
|
||||
</TabPanels>
|
||||
</TabGroup>
|
||||
|
||||
<Dialog open={isOpen} on:close={() => (isOpen = false)}>
|
||||
<DialogOverlay />
|
||||
|
||||
<DialogTitle>Deactivate account</DialogTitle>
|
||||
<DialogDescription>This will permanently deactivate your account</DialogDescription>
|
||||
|
||||
<p>
|
||||
Are you sure you want to deactivate your account? All of your data will be permanently removed.
|
||||
This action cannot be undone.
|
||||
</p>
|
||||
|
||||
<button on:click={() => (isOpen = false)}>Deactivate</button>
|
||||
<button on:click={() => (isOpen = false)}>Cancel</button>
|
||||
</Dialog>
|
||||
|
|
|
@ -3,6 +3,6 @@ import type { PageLoad } from './$types';
|
|||
|
||||
export const load: PageLoad = async (event) => {
|
||||
const session = (await event.parent()).session;
|
||||
if (!session?.user) throw redirect(303, '/auth');
|
||||
if (!session?.user) throw redirect(303, '/auth/signin');
|
||||
return { session: session };
|
||||
};
|
||||
|
|
44
src/routes/create/FlattenedFileList.svelte
Normal file
44
src/routes/create/FlattenedFileList.svelte
Normal file
|
@ -0,0 +1,44 @@
|
|||
<script lang="ts">
|
||||
import type { FileTree, File } from '$lib/types';
|
||||
import { metafileSchema } from '$lib/packwiz-types';
|
||||
import toml from '@ltd/j-toml';
|
||||
export let tree: FileTree;
|
||||
function getFilesRecursively(tree: FileTree): [string[], File][] {
|
||||
if (tree.type == 'file') {
|
||||
return [[[], tree]];
|
||||
}
|
||||
let result: [string[], File][] = [];
|
||||
for (const child in tree.children) {
|
||||
if (Object.prototype.hasOwnProperty.call(tree.children, child)) {
|
||||
const element = tree.children[child];
|
||||
result = result.concat(
|
||||
getFilesRecursively(element).map(([path, file]) => [[child].concat(path), file])
|
||||
);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
</script>
|
||||
|
||||
<ul>
|
||||
{#each getFilesRecursively(tree) as mod (mod[0])}
|
||||
<li>
|
||||
{#if mod[1].inline && mod[1].metafile}
|
||||
{@const parsed = metafileSchema.parse(toml.parse(mod[1].content))}
|
||||
<h2>{parsed.name} ({mod[0].join('/')})</h2>
|
||||
{#if parsed.update.curseforge}
|
||||
<a
|
||||
href="https://minecraft.curseforge.com/projects/{parsed.update.curseforge[
|
||||
'project-id'
|
||||
]}">Curseforge</a
|
||||
>
|
||||
{/if}
|
||||
{#if parsed.update.modrinth}
|
||||
<a href="https://modrinth.com/project/{parsed.update.modrinth['mod-id']}">Modrinth</a>
|
||||
{/if}
|
||||
{:else}
|
||||
<h2>{mod[0].join('/')}</h2>
|
||||
{/if}
|
||||
</li>
|
||||
{/each}
|
||||
</ul>
|
15
yarn.lock
15
yarn.lock
|
@ -195,6 +195,11 @@
|
|||
resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz#b520529ec21d8e5945a1851dfd1c32e94e39ff45"
|
||||
integrity sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==
|
||||
|
||||
"@iarna/toml@^2.2.5":
|
||||
version "2.2.5"
|
||||
resolved "https://registry.yarnpkg.com/@iarna/toml/-/toml-2.2.5.tgz#b32366c89b43c6f8cefbdefac778b9c828e3ba8c"
|
||||
integrity sha512-trnsAYxU3xnS1gPHPyU961coFyLkh4gAD/0zQ5mymY4yOZ+CYvsPqUbOFSw0aDM4y0tV7tiFxL/1XfXPNC6IPg==
|
||||
|
||||
"@jridgewell/resolve-uri@3.1.0":
|
||||
version "3.1.0"
|
||||
resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz#2203b118c157721addfe69d47b70465463066d78"
|
||||
|
@ -213,6 +218,11 @@
|
|||
"@jridgewell/resolve-uri" "3.1.0"
|
||||
"@jridgewell/sourcemap-codec" "1.4.14"
|
||||
|
||||
"@ltd/j-toml@^1.38.0":
|
||||
version "1.38.0"
|
||||
resolved "https://registry.yarnpkg.com/@ltd/j-toml/-/j-toml-1.38.0.tgz#00d19f6d65ac5dac39bc64f97a545f47e9ebefc4"
|
||||
integrity sha512-lYtBcmvHustHQtg4X7TXUu1Xa/tbLC3p2wLvgQI+fWVySguVZJF60Snxijw5EiohumxZbR10kWYFFebh1zotiw==
|
||||
|
||||
"@nodelib/fs.scandir@2.1.5":
|
||||
version "2.1.5"
|
||||
resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz#7619c2eb21b25483f6d167548b4cfd5a7488c3d5"
|
||||
|
@ -2906,11 +2916,6 @@ to-regex-range@^5.0.1:
|
|||
dependencies:
|
||||
is-number "^7.0.0"
|
||||
|
||||
toml@^3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/toml/-/toml-3.0.0.tgz#342160f1af1904ec9d204d03a5d61222d762c5ee"
|
||||
integrity sha512-y/mWCZinnvxjTKYhJ+pYxwD0mRLVvOtdS2Awbgxln6iEnt4rk0yBxeSBHkGJcPucRiG0e55mwWp+g/05rsrd6w==
|
||||
|
||||
totalist@^3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/totalist/-/totalist-3.0.0.tgz#4ef9c58c5f095255cdc3ff2a0a55091c57a3a1bd"
|
||||
|
|
Loading…
Reference in a new issue