diff --git a/.example.env b/.example.env
new file mode 100644
index 0000000..b864612
--- /dev/null
+++ b/.example.env
@@ -0,0 +1,5 @@
+AWS_ACCESS_KEY_ID=**************************
+AWS_SECRET_ACCESS_KEY=****************************************************************
+AWS_DEFAULT_REGION=******
+AWS_ENDPOINT_URL=localhost:3000
+S3_DEFAULT_BUCKET=comicverse-test-bucket
diff --git a/.gitignore b/.gitignore
index 79518f7..68c23fa 100644
--- a/.gitignore
+++ b/.gitignore
@@ -19,3 +19,5 @@ Thumbs.db
# Vite
vite.config.js.timestamp-*
vite.config.ts.timestamp-*
+
+data.db
diff --git a/bun.lockb b/bun.lockb
index d1e9d95..21ce4e2 100755
Binary files a/bun.lockb and b/bun.lockb differ
diff --git a/flake.nix b/flake.nix
index 5be7a89..38e9896 100644
--- a/flake.nix
+++ b/flake.nix
@@ -22,10 +22,11 @@
devShells = forAllSystems (system: pkgs: {
default = pkgs.mkShell {
buildInputs = with pkgs; [
+ awscli2
+ bun
eslint
nodejs_22
nodePackages_latest.prettier
- bun
];
};
});
diff --git a/package.json b/package.json
index f8ef5f0..027ffd1 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,28 @@
{
"name": "comicverse",
"version": "0.0.1",
+ "devDependencies": {
+ "@sveltejs/adapter-auto": "^3.0.0",
+ "@sveltejs/kit": "^2.0.0",
+ "@sveltejs/vite-plugin-svelte": "^3.0.0",
+ "@types/eslint": "^9.6.0",
+ "@types/node": "^22.6.0",
+ "eslint": "^9.0.0",
+ "eslint-config-prettier": "^9.1.0",
+ "eslint-plugin-svelte": "^2.36.0",
+ "globals": "^15.0.0",
+ "minio": "^8.0.1",
+ "prettier": "^3.1.1",
+ "prettier-plugin-svelte": "^3.1.2",
+ "sqlite": "^5.1.1",
+ "sqlite3": "^5.1.7",
+ "svelte": "^4.2.7",
+ "svelte-check": "^4.0.0",
+ "typescript": "^5.0.0",
+ "typescript-eslint": "^8.0.0",
+ "vite": "^5.0.3",
+ "xml-js": "^1.6.11"
+ },
"private": true,
"scripts": {
"dev": "vite dev",
@@ -11,22 +33,5 @@
"lint": "prettier --check . && eslint .",
"format": "prettier --write ."
},
- "devDependencies": {
- "@sveltejs/adapter-auto": "^3.0.0",
- "@sveltejs/kit": "^2.0.0",
- "@sveltejs/vite-plugin-svelte": "^3.0.0",
- "@types/eslint": "^9.6.0",
- "eslint": "^9.0.0",
- "eslint-config-prettier": "^9.1.0",
- "eslint-plugin-svelte": "^2.36.0",
- "globals": "^15.0.0",
- "prettier": "^3.1.1",
- "prettier-plugin-svelte": "^3.1.2",
- "svelte": "^4.2.7",
- "svelte-check": "^4.0.0",
- "typescript": "^5.0.0",
- "typescript-eslint": "^8.0.0",
- "vite": "^5.0.3"
- },
"type": "module"
}
diff --git a/src/app.html b/src/app.html
index 77a5ff5..bc40f4a 100644
--- a/src/app.html
+++ b/src/app.html
@@ -1,5 +1,5 @@
-
+
diff --git a/src/lib/index.ts b/src/lib/index.ts
index 856f2b6..e655f2b 100644
--- a/src/lib/index.ts
+++ b/src/lib/index.ts
@@ -1 +1,3 @@
-// place files you want to import through the `$lib` alias in this folder.
+export { default as s3 } from './s3';
+export { default as db } from './sqlite';
+export * from './sqlite'
diff --git a/src/lib/s3.ts b/src/lib/s3.ts
new file mode 100644
index 0000000..e8ded2b
--- /dev/null
+++ b/src/lib/s3.ts
@@ -0,0 +1,19 @@
+import {
+ AWS_ENDPOINT_URL,
+ AWS_ACCESS_KEY_ID,
+ AWS_DEFAULT_REGION,
+ AWS_SECRET_ACCESS_KEY
+} from '$env/static/private';
+
+import * as Minio from 'minio';
+
+const client = new Minio.Client({
+ endPoint: AWS_ENDPOINT_URL.split(':')[0],
+ port: Number(AWS_ENDPOINT_URL.split(':')[1]),
+ useSSL: false,
+ region: AWS_DEFAULT_REGION,
+ accessKey: AWS_ACCESS_KEY_ID,
+ secretKey: AWS_SECRET_ACCESS_KEY
+});
+
+export default client;
diff --git a/src/lib/sqlite.ts b/src/lib/sqlite.ts
new file mode 100644
index 0000000..f6affd3
--- /dev/null
+++ b/src/lib/sqlite.ts
@@ -0,0 +1,28 @@
+import sqlite3 from 'sqlite3';
+import { open } from 'sqlite';
+
+const db = await open({
+ filename: 'data.db',
+ driver: sqlite3.cached.Database
+});
+
+await db.exec(`
+CREATE TABLE IF NOT EXISTS projects (
+ ID text NOT NULL,
+ Name text NOT NULL,
+ PRIMARY KEY(ID)
+)
+`);
+
+type Project = {
+ id: string;
+ title: string;
+ pages: {
+ title: string;
+ src: string;
+ background: string;
+ }[];
+};
+
+export type { Project };
+export default db;
diff --git a/src/routes/+layout.svelte b/src/routes/+layout.svelte
new file mode 100644
index 0000000..ad4b45a
--- /dev/null
+++ b/src/routes/+layout.svelte
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/routes/+page.server.ts b/src/routes/+page.server.ts
new file mode 100644
index 0000000..53a4103
--- /dev/null
+++ b/src/routes/+page.server.ts
@@ -0,0 +1,40 @@
+import { fail, type Actions } from '@sveltejs/kit';
+import type { PageServerLoad } from './$types';
+
+import { db, s3, type Project } from '$lib';
+import { AWS_S3_DEFAULT_BUCKET } from '$env/static/private';
+
+export const load = (async ({}) => {
+ const res = await db.all('SELECT ID, Name FROM projects');
+ return { projects: res };
+}) as PageServerLoad;
+
+export const actions: Actions = {
+ default: async ({ request }) => {
+ const data = await request.formData();
+ const name = data.get('project-name');
+
+ if (!name) return fail(400, { name, missing: true });
+
+ const uuid = crypto.randomUUID().split('-')[0];
+
+ const res = await db.run('INSERT OR IGNORE INTO projects (ID, Name) VALUES (:id, :name)', {
+ ':id': uuid,
+ ':name': name
+ });
+
+ const project: Project = {
+ id: uuid,
+ title: name.toString(),
+ pages: []
+ };
+
+ await s3.putObject(AWS_S3_DEFAULT_BUCKET, `${uuid}/project.json`, JSON.stringify(project));
+
+ if (res.changes == undefined) {
+ return fail(500, { reason: 'Failed to insert project into database' });
+ }
+
+ return { success: true };
+ }
+};
diff --git a/src/routes/+page.svelte b/src/routes/+page.svelte
index 5982b0a..5ee34b4 100644
--- a/src/routes/+page.svelte
+++ b/src/routes/+page.svelte
@@ -1,2 +1,46 @@
-Welcome to SvelteKit
-Visit kit.svelte.dev to read the documentation
+
+
+
+ {#each data.projects as p}
+
+
+ {p.ID}
+
+ {/each}
+
+
+
+
+
+
diff --git a/src/routes/files/[project]/[file]/+server.ts b/src/routes/files/[project]/[file]/+server.ts
new file mode 100644
index 0000000..55b2dcc
--- /dev/null
+++ b/src/routes/files/[project]/[file]/+server.ts
@@ -0,0 +1,41 @@
+import { type RequestHandler } from '@sveltejs/kit';
+import stream from 'node:stream/promises';
+
+import { db, s3, type Project } from '$lib';
+import { AWS_S3_DEFAULT_BUCKET } from '$env/static/private';
+import { extname } from 'node:path';
+
+export const GET = (async ({ params }) => {
+ const file = await s3.getObject(AWS_S3_DEFAULT_BUCKET, `${params.project}/${params.file}`);
+ file.on('error', (err: any) => {
+ console.log(err);
+ });
+
+ let chunks: Buffer[] = [];
+ let buf;
+
+ file.on('data', (chunk) => {
+ chunks.push(Buffer.from(chunk));
+ });
+ file.on('end', () => {
+ buf = Buffer.concat(chunks);
+ });
+ await stream.finished(file)
+
+ let res = new Response(buf);
+ res.headers.set(
+ 'Content-Type',
+ (() => {
+ switch (extname(params.file!)) {
+ case '.png':
+ return 'image/png';
+ case '.json':
+ return 'application/json';
+ }
+ return 'text/plain';
+ })()
+ );
+ res.headers.set('Cache-Control', 'max-age=604800')
+
+ return res;
+}) as RequestHandler;
diff --git a/src/routes/projects/[id]/+page.server.ts b/src/routes/projects/[id]/+page.server.ts
new file mode 100644
index 0000000..f713ec4
--- /dev/null
+++ b/src/routes/projects/[id]/+page.server.ts
@@ -0,0 +1,76 @@
+import { error, fail, redirect, type Actions } from '@sveltejs/kit';
+import type { PageServerLoad } from './$types';
+import stream from 'node:stream/promises';
+import { db, s3, type Project } from '$lib';
+import { AWS_S3_DEFAULT_BUCKET } from '$env/static/private';
+import { extname } from 'node:path';
+
+export const load = (async ({ params }) => {
+ const res = await db.get<{ id: string; name: string }>(
+ 'SELECT ID, Name FROM projects WHERE ID = ?',
+ params.id
+ );
+
+ if (res === undefined) {
+ return fail(404, { reason: 'Failed to find project into database' });
+ }
+
+ const project = await s3.getObject(AWS_S3_DEFAULT_BUCKET, `${params.id}/project.json`);
+ project.on('error', (err: any) => {
+ console.log(err);
+ });
+ let p: string = '';
+ project.on('data', (chunk: any) => {
+ p += chunk;
+ });
+ await stream.finished(project);
+ let proj = JSON.parse(p) as Project;
+
+ return { project: proj };
+}) as PageServerLoad;
+
+export const actions = {
+ delete: async ({ params }) => {
+ const res = await db.run('DELETE FROM projects WHERE ID = ?', params.id);
+
+ await s3.removeObject(AWS_S3_DEFAULT_BUCKET, `${params.id}/project.json`);
+
+ if (res === undefined) {
+ return fail(500, { reason: 'Failed to delete project' });
+ }
+
+ redirect(303, '/');
+ },
+ addpage: async ({ request, params }) => {
+ const form = await request.formData();
+ const file = form?.get('file') as File;
+ const title = form?.get('title') as string;
+ const color = form?.get('color') as string;
+
+ console.log(file);
+
+ const project = await s3.getObject(AWS_S3_DEFAULT_BUCKET, `${params.id}/project.json`);
+ project.on('error', (err: any) => {
+ console.log(err);
+ });
+ let p: string = '';
+ project.on('data', (chunk: any) => {
+ p += chunk;
+ });
+ await stream.finished(project);
+ let proj = JSON.parse(p) as Project;
+
+ const filename = `${crypto.randomUUID().split('-')[0]}${extname(file?.name)}`;
+
+ proj.pages.push({
+ title: title,
+ src: filename,
+ background: color
+ });
+
+ const buf = Buffer.from(await file.arrayBuffer());
+
+ await s3.putObject(AWS_S3_DEFAULT_BUCKET, `${params.id}/project.json`, JSON.stringify(proj));
+ await s3.putObject(AWS_S3_DEFAULT_BUCKET, `${params.id}/${filename}`, buf);
+ }
+} as Actions;
diff --git a/src/routes/projects/[id]/+page.svelte b/src/routes/projects/[id]/+page.svelte
new file mode 100644
index 0000000..79cc3c6
--- /dev/null
+++ b/src/routes/projects/[id]/+page.svelte
@@ -0,0 +1,145 @@
+
+
+
+{JSON.stringify(
+ {
+ color: color,
+ scroll: scroll
+ },
+ null,
+ 2
+ )}
+
+
+
+
+
+
+ (scroll = reader.scrollTop)}
+ >
+
+ {#each pages as page}
+
+

+
+
+ {/each}
+
+
+
+
+
diff --git a/temp.json b/temp.json
new file mode 100644
index 0000000..b6fbdff
--- /dev/null
+++ b/temp.json
@@ -0,0 +1 @@
+{"id":"88ba21ea","title":"test project","pages":[]}
\ No newline at end of file
diff --git a/vite.config.ts b/vite.config.ts
index bbf8c7d..efed4ca 100644
--- a/vite.config.ts
+++ b/vite.config.ts
@@ -2,5 +2,9 @@ import { sveltekit } from '@sveltejs/kit/vite';
import { defineConfig } from 'vite';
export default defineConfig({
- plugins: [sveltekit()]
+ plugins: [sveltekit()],
+ server: {
+ port: 3000,
+ host: '192.168.1.7'
+ }
});