mirror of
https://github.com/NebulaServices/Nebula.git
synced 2025-05-12 19:40:02 -04:00
Server: switch to Fastify
Switch config to TOML over JSON (it's just nicer TBH)
This commit is contained in:
parent
deda273eda
commit
620ad25c61
20 changed files with 1373 additions and 683 deletions
6
.gitignore
vendored
6
.gitignore
vendored
|
@ -1,11 +1,13 @@
|
||||||
# build output
|
# build output
|
||||||
dist/
|
dist/
|
||||||
|
server/*.js
|
||||||
|
|
||||||
# generated types
|
# generated types
|
||||||
.astro/
|
.astro/
|
||||||
|
|
||||||
# dependencies
|
# dependencies
|
||||||
node_modules/
|
node_modules/
|
||||||
|
package-lock.json
|
||||||
|
|
||||||
# logs
|
# logs
|
||||||
npm-debug.log*
|
npm-debug.log*
|
||||||
|
@ -26,5 +28,9 @@ pnpm-debug.log*
|
||||||
# nebula catalog database
|
# nebula catalog database
|
||||||
database.sqlite
|
database.sqlite
|
||||||
|
|
||||||
|
|
||||||
|
# YOUR config
|
||||||
|
config.toml
|
||||||
|
|
||||||
# Goofy PNPM problem
|
# Goofy PNPM problem
|
||||||
~/
|
~/
|
||||||
|
|
|
@ -191,9 +191,10 @@ docker compose -f ./docker-compose.build.yml build
|
||||||
```
|
```
|
||||||
---
|
---
|
||||||
|
|
||||||
## Environment
|
## Config
|
||||||
|
|
||||||
- There are a couple of environment variables for nebula. Most of the time, the defaults are fine, but there are instances where you may not want certain options enabled or certain things running.
|
- There are a couple of configuration options for nebula. Most of the time, the defaults are fine, but there are instances where you may not want certain options enabled or certain things running.
|
||||||
|
- An example config file is located [here](./config.example.toml). Config format is TOML
|
||||||
|
|
||||||
| Variable | Description | Default |
|
| Variable | Description | Default |
|
||||||
|------------------------|----------------------------------------------------------------------------------------------------------|---------|
|
|------------------------|----------------------------------------------------------------------------------------------------------|---------|
|
||||||
|
|
|
@ -75,6 +75,6 @@ export default defineConfig({
|
||||||
},
|
},
|
||||||
output: "server",
|
output: "server",
|
||||||
adapter: node({
|
adapter: node({
|
||||||
mode: "hybrid"
|
mode: "middleware"
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
|
24
config.example.toml
Normal file
24
config.example.toml
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
[marketplace]
|
||||||
|
enabled = true # Turn on or off the marketplace entirely
|
||||||
|
psk = "CHANGEME" # Change this to something more secure.
|
||||||
|
level = 1
|
||||||
|
|
||||||
|
[db]
|
||||||
|
name = "database" # Your databsae name
|
||||||
|
username = "username" # The username of your DB (SQLITE just ignores this)
|
||||||
|
password = "password" # The password to your DB (SQLITE ignores this)
|
||||||
|
postgres = false # Enable to use postgres over sqlite (recommended for large prod instances)
|
||||||
|
|
||||||
|
[postgres] # Set the "domain" to either and ip address or a actual domain
|
||||||
|
domain = ""
|
||||||
|
port = 5432
|
||||||
|
|
||||||
|
[server.server]
|
||||||
|
port = 8080
|
||||||
|
wisp = true
|
||||||
|
logging = true # Disable for the tons & tons of logs to go away (useful for debugging but otherwise eh)
|
||||||
|
|
||||||
|
[server.rammerhead] # Don't touch this section unless you KNOW what you are doing
|
||||||
|
reverseproxy = true
|
||||||
|
localstorage_sync = true
|
||||||
|
http2 = true
|
|
@ -1,5 +0,0 @@
|
||||||
{
|
|
||||||
"marketplace_enabled": true,
|
|
||||||
"marketplace_psk": "CHANGE_THIS_THIS_IS_INSECURE",
|
|
||||||
"marketplace_level": "1"
|
|
||||||
}
|
|
41
package.json
41
package.json
|
@ -3,10 +3,12 @@
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"version": "0.0.1",
|
"version": "0.0.1",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "concurrently \"astro dev\" \"node server.js\"",
|
"dev": "concurrently \"astro dev\" \"tsx --watch server/server.ts\"",
|
||||||
"start": "node server.js",
|
"start": "node server/server.js",
|
||||||
"build": "astro check && astro build",
|
"build:server": "tsc -p server",
|
||||||
"bstart": "astro build && node server.js",
|
"build:client": "astro check && astro build",
|
||||||
|
"build": "concurrently \"npm:build:server\" \"npm:build:client\"",
|
||||||
|
"bstart": "npm run build && npm run start",
|
||||||
"preview": "astro preview",
|
"preview": "astro preview",
|
||||||
"astro": "astro",
|
"astro": "astro",
|
||||||
"format:code": "biome format . --write",
|
"format:code": "biome format . --write",
|
||||||
|
@ -16,11 +18,13 @@
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@astrojs/check": "^0.8.3",
|
"@astrojs/check": "^0.8.3",
|
||||||
"@astrojs/node": "^8.3.4",
|
"@astrojs/node": "^8.3.4",
|
||||||
"@astrojs/svelte": "^5.7.1",
|
"@astrojs/svelte": "^5.7.2",
|
||||||
"@astrojs/tailwind": "^5.1.1",
|
"@astrojs/tailwind": "^5.1.2",
|
||||||
"@fastify/compress": "^7.0.3",
|
"@fastify/compress": "^8.0.1",
|
||||||
"@fastify/static": "^7.0.4",
|
"@fastify/middie": "^9.0.2",
|
||||||
"@iconify-json/ph": "^1.2.0",
|
"@fastify/multipart": "^9.0.1",
|
||||||
|
"@fastify/static": "^8.0.1",
|
||||||
|
"@iconify-json/ph": "^1.2.1",
|
||||||
"@mercuryworkshop/bare-mux": "1.1.1",
|
"@mercuryworkshop/bare-mux": "1.1.1",
|
||||||
"@mercuryworkshop/epoxy-transport": "github:motortruck1221/epoxytransport",
|
"@mercuryworkshop/epoxy-transport": "github:motortruck1221/epoxytransport",
|
||||||
"@mercuryworkshop/libcurl-transport": "^1.3.10",
|
"@mercuryworkshop/libcurl-transport": "^1.3.10",
|
||||||
|
@ -28,27 +32,34 @@
|
||||||
"@rubynetwork/rammerhead-browser": "^1.0.9",
|
"@rubynetwork/rammerhead-browser": "^1.0.9",
|
||||||
"@svelte-drama/suspense": "0.5.1",
|
"@svelte-drama/suspense": "0.5.1",
|
||||||
"@titaniumnetwork-dev/ultraviolet": "3.1.2",
|
"@titaniumnetwork-dev/ultraviolet": "3.1.2",
|
||||||
"astro": "^4.15.11",
|
"@types/node": "^22.7.5",
|
||||||
|
"@types/sequelize": "^4.28.20",
|
||||||
|
"astro": "^4.16.2",
|
||||||
"astro-icon": "^1.1.1",
|
"astro-icon": "^1.1.1",
|
||||||
|
"chalk": "^5.3.0",
|
||||||
"concurrently": "^8.2.2",
|
"concurrently": "^8.2.2",
|
||||||
"express": "^4.21.0",
|
"fastify": "^5.0.0",
|
||||||
"fastify": "^4.28.1",
|
"form-data": "^4.0.1",
|
||||||
"form-data": "^4.0.0",
|
|
||||||
"formdata-node": "^6.0.3",
|
"formdata-node": "^6.0.3",
|
||||||
|
"gradient-string": "^3.0.0",
|
||||||
"libcurl.js-new": "npm:libcurl.js@^0.6.16",
|
"libcurl.js-new": "npm:libcurl.js@^0.6.16",
|
||||||
"multer": "1.4.5-lts.1",
|
"multer": "1.4.5-lts.1",
|
||||||
"nanostores": "^0.10.3",
|
"nanostores": "^0.10.3",
|
||||||
|
"pg": "^8.13.0",
|
||||||
|
"pg-hstore": "^2.3.4",
|
||||||
"sequelize": "^6.37.4",
|
"sequelize": "^6.37.4",
|
||||||
|
"smol-toml": "^1.3.0",
|
||||||
"sqlite3": "^5.1.7",
|
"sqlite3": "^5.1.7",
|
||||||
"svelte": "^4.2.19",
|
"svelte": "^4.2.19",
|
||||||
"svelte-french-toast": "^1.2.0",
|
"svelte-french-toast": "^1.2.0",
|
||||||
"tailwindcss": "^3.4.13",
|
"tailwindcss": "^3.4.13",
|
||||||
"typescript": "^5.6.2",
|
"typescript": "^5.6.3",
|
||||||
"vite-plugin-static-copy": "^1.0.6",
|
"vite-plugin-static-copy": "^1.0.6",
|
||||||
"wisp-server-node": "^1.1.7"
|
"wisp-server-node": "^1.1.7"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@biomejs/biome": "^1.9.3",
|
"@biomejs/biome": "^1.9.3",
|
||||||
"bufferutil": "^4.0.8"
|
"bufferutil": "^4.0.8",
|
||||||
|
"tsx": "^4.19.1"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
1352
pnpm-lock.yaml
generated
1352
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load diff
82
server/config.ts
Normal file
82
server/config.ts
Normal file
|
@ -0,0 +1,82 @@
|
||||||
|
import { readFileSync } from 'node:fs';
|
||||||
|
import { parse, TomlPrimitive } from 'smol-toml';
|
||||||
|
import { fileURLToPath } from 'node:url';
|
||||||
|
import chalk from 'chalk';
|
||||||
|
|
||||||
|
interface TomlData {
|
||||||
|
marketplace: {
|
||||||
|
enabled: boolean;
|
||||||
|
psk: String
|
||||||
|
}
|
||||||
|
server: {
|
||||||
|
server: {
|
||||||
|
port: number;
|
||||||
|
wisp: boolean;
|
||||||
|
logging: boolean;
|
||||||
|
}
|
||||||
|
rammerhead: {
|
||||||
|
reverseproxy: boolean;
|
||||||
|
localstorage_sync: boolean;
|
||||||
|
http2: boolean;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
db: {
|
||||||
|
name: string;
|
||||||
|
username: string;
|
||||||
|
password: string;
|
||||||
|
postgres: boolean;
|
||||||
|
},
|
||||||
|
postgres: {
|
||||||
|
domain: string;
|
||||||
|
port: number;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Verify {
|
||||||
|
name: string,
|
||||||
|
typeOF: any,
|
||||||
|
type: any
|
||||||
|
}
|
||||||
|
|
||||||
|
let doc = readFileSync(fileURLToPath(new URL('../config.toml', import.meta.url))).toString();
|
||||||
|
const parsedDoc = parse(doc) as unknown as TomlData;
|
||||||
|
|
||||||
|
function verify(t: Verify[]) {
|
||||||
|
for (let i: number = 0; i !== t.length; i++) {
|
||||||
|
if (typeof t[i].typeOF !== t[i].type) {
|
||||||
|
throw new Error(`Invalid structure: "${t[i].name}" should be a(n) ${t[i].type}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
verify([
|
||||||
|
{name: 'marketplace', typeOF: parsedDoc.marketplace, type: 'object'},
|
||||||
|
{name: 'marketplace.enabled', typeOF: parsedDoc.marketplace.enabled, type: 'boolean'},
|
||||||
|
{name: 'marketplace.psk', typeOF: parsedDoc.marketplace.psk, type: 'string'},
|
||||||
|
{name: 'server', typeOF: parsedDoc.server, type: 'object'},
|
||||||
|
{name: 'server.server', typeOF: parsedDoc.server.server, type: 'object'},
|
||||||
|
{name: 'server.rammerhead', typeOF: parsedDoc.server.rammerhead, type: 'object'},
|
||||||
|
{name: 'server.server.port', typeOF: parsedDoc.server.server.port, type: 'number'},
|
||||||
|
{name: 'server.server.wisp', typeOF: parsedDoc.server.server.wisp, type: 'boolean'},
|
||||||
|
{name: 'server.server.logging', typeOF: parsedDoc.server.server.logging, type: 'boolean'},
|
||||||
|
{name: 'server.rammerhead.reverseproxy', typeOF: parsedDoc.server.rammerhead.reverseproxy, type: 'boolean'},
|
||||||
|
{name: 'server.rammerhead.localstorage_sync', typeOF: parsedDoc.server.rammerhead.localstorage_sync, type: 'boolean'},
|
||||||
|
{name: 'server.rammerhead.http2', typeOF: parsedDoc.server.rammerhead.http2, type: 'boolean'},
|
||||||
|
{name: 'db', typeOF: parsedDoc.db, type: 'object'},
|
||||||
|
{name: 'db.name', typeOF: parsedDoc.db.name, type: 'string'},
|
||||||
|
{name: 'db.username', typeOF: parsedDoc.db.username, type: 'string'},
|
||||||
|
{name: 'db.password', typeOF: parsedDoc.db.password, type: 'string'},
|
||||||
|
{name: 'db.postgres', typeOF: parsedDoc.db.postgres, type: 'boolean'},
|
||||||
|
{name: 'postgres', typeOF: parsedDoc.postgres, type: 'object'},
|
||||||
|
{name: 'postgres.domain', typeOF: parsedDoc.postgres.domain, type: 'string'},
|
||||||
|
{name: 'postgres.port', typeOF: parsedDoc.postgres.port, type: 'number'}
|
||||||
|
]);
|
||||||
|
|
||||||
|
if (parsedDoc.marketplace.psk === "CHANGEME") {
|
||||||
|
console.warn(chalk.yellow.bold('PSK should be changed from "CHANGEME"'));
|
||||||
|
}
|
||||||
|
if (parsedDoc.db.password === "password") {
|
||||||
|
console.warn(chalk.red.bold('You should change your DB password!!'));
|
||||||
|
}
|
||||||
|
|
||||||
|
export { TomlData, parsedDoc }
|
23
server/dbSetup.ts
Normal file
23
server/dbSetup.ts
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
import chalk from "chalk";
|
||||||
|
import { CatalogModel } from "./server.js";
|
||||||
|
import { ModelCtor } from "sequelize";
|
||||||
|
|
||||||
|
function setupDB(db: ModelCtor<CatalogModel>) {
|
||||||
|
//We have some packages that need to be installed if they aren't.
|
||||||
|
//TODO: set this up
|
||||||
|
console.log(chalk.hex('#7967dd')('Performing DB setup...'));
|
||||||
|
//db.create({
|
||||||
|
// package_name: 'com.nebula.cybermonay',
|
||||||
|
// title: 'Cyber Monay',
|
||||||
|
// image: 'cyber_monay.jpg',
|
||||||
|
// author: 'Nebula Services',
|
||||||
|
// version: '1.0.0',
|
||||||
|
// description: 'A parody of the famous "Cyber Monay" hack!',
|
||||||
|
// tags: ["Hacking", "Animated", "Funny"],
|
||||||
|
// payload: "com.nebula.cybermonay.css",
|
||||||
|
// background_video: "cyber_monay_test.mp4",
|
||||||
|
// type: 'theme'
|
||||||
|
//});
|
||||||
|
}
|
||||||
|
|
||||||
|
export { setupDB }
|
1
server/env.d.ts
vendored
Normal file
1
server/env.d.ts
vendored
Normal file
|
@ -0,0 +1 @@
|
||||||
|
declare module '@rubynetwork/rammerhead/src/server/index.js';
|
264
server/server.ts
Normal file
264
server/server.ts
Normal file
|
@ -0,0 +1,264 @@
|
||||||
|
import { fileURLToPath } from "node:url";
|
||||||
|
import fastifyCompress from "@fastify/compress";
|
||||||
|
import fastifyMiddie from "@fastify/middie";
|
||||||
|
import fastifyStatic from "@fastify/static";
|
||||||
|
import fastifyMultipart from "@fastify/multipart";
|
||||||
|
import Fastify, { FastifyReply, FastifyRequest } from 'fastify';
|
||||||
|
import chalk from 'chalk';
|
||||||
|
import { serverFactory } from "./serverFactory.js";
|
||||||
|
import { handler as ssrHandler } from "../dist/server/entry.mjs";
|
||||||
|
import gradient from "gradient-string";
|
||||||
|
import { parsedDoc } from "./config.js";
|
||||||
|
import { DataTypes, InferAttributes, InferCreationAttributes, Model, Sequelize } from "sequelize";
|
||||||
|
import { pipeline } from "node:stream/promises";
|
||||||
|
import { createWriteStream } from "node:fs";
|
||||||
|
import { setupDB } from "./dbSetup.js";
|
||||||
|
|
||||||
|
|
||||||
|
const app = Fastify({ logger: parsedDoc.server.server.logging, ignoreDuplicateSlashes: true, ignoreTrailingSlash: true, serverFactory: serverFactory });
|
||||||
|
const db = new Sequelize(parsedDoc.db.name, parsedDoc.db.username, parsedDoc.db.password, {
|
||||||
|
host: parsedDoc.db.postgres ? `${parsedDoc.postgres.domain}` : 'localhost',
|
||||||
|
port: parsedDoc.db.postgres ? parsedDoc.postgres.port : undefined,
|
||||||
|
dialect: parsedDoc.db.postgres ? 'postgres': 'sqlite',
|
||||||
|
logging: parsedDoc.server.server.logging,
|
||||||
|
storage: 'database.sqlite' //this is sqlite only
|
||||||
|
});
|
||||||
|
|
||||||
|
interface CatalogModel extends Model<InferAttributes<CatalogModel>, InferCreationAttributes<CatalogModel>> {
|
||||||
|
package_name: string
|
||||||
|
title: string
|
||||||
|
description: string
|
||||||
|
author: string
|
||||||
|
image: string
|
||||||
|
tags: object
|
||||||
|
version: string
|
||||||
|
background_image: string
|
||||||
|
background_video: string
|
||||||
|
payload: string
|
||||||
|
type: string
|
||||||
|
}
|
||||||
|
const catalogAssets = db.define<CatalogModel>("catalog_assets", {
|
||||||
|
package_name: { type: DataTypes.TEXT, unique: true },
|
||||||
|
title: { type: DataTypes.TEXT },
|
||||||
|
description: { type: DataTypes.TEXT },
|
||||||
|
author: { type: DataTypes.TEXT },
|
||||||
|
image: { type: DataTypes.TEXT },
|
||||||
|
tags: { type: DataTypes.JSON, allowNull: true },
|
||||||
|
version: { type: DataTypes.TEXT },
|
||||||
|
background_image: { type: DataTypes.TEXT, allowNull: true },
|
||||||
|
background_video: { type: DataTypes.TEXT, allowNull: true },
|
||||||
|
payload: { type: DataTypes.TEXT },
|
||||||
|
type: { type: DataTypes.TEXT }
|
||||||
|
});
|
||||||
|
|
||||||
|
await app.register(fastifyCompress, {
|
||||||
|
encodings: ['br', 'gzip', 'deflate']
|
||||||
|
});
|
||||||
|
|
||||||
|
await app.register(fastifyMultipart);
|
||||||
|
|
||||||
|
await app.register(fastifyStatic, {
|
||||||
|
root: fileURLToPath(new URL('../dist/client', import.meta.url)),
|
||||||
|
decorateReply: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
await app.register(fastifyStatic, {
|
||||||
|
root: fileURLToPath(new URL('../database_assets/image', import.meta.url)),
|
||||||
|
prefix: '/images/',
|
||||||
|
decorateReply: false
|
||||||
|
});
|
||||||
|
await app.register(fastifyStatic, {
|
||||||
|
root: fileURLToPath(new URL('../database_assets/video', import.meta.url)),
|
||||||
|
prefix: '/videos/',
|
||||||
|
decorateReply: false
|
||||||
|
});
|
||||||
|
await app.register(fastifyStatic, {
|
||||||
|
root: fileURLToPath(new URL('../database_assets/styles', import.meta.url)),
|
||||||
|
prefix: '/styles/',
|
||||||
|
decorateReply: false
|
||||||
|
});
|
||||||
|
await app.register(fastifyStatic, {
|
||||||
|
root: fileURLToPath(new URL('../database_assets/scripts', import.meta.url)),
|
||||||
|
prefix: '/scripts/',
|
||||||
|
decorateReply: false
|
||||||
|
});
|
||||||
|
|
||||||
|
await app.register(fastifyMiddie);
|
||||||
|
|
||||||
|
app.get("/api", (request, reply) => {
|
||||||
|
reply.send({ Server: 'Active' });
|
||||||
|
});
|
||||||
|
|
||||||
|
// This API returns a list of the assets in the database (SW plugins and themes).
|
||||||
|
// It also returns the number of pages in the database.
|
||||||
|
// It can take a `?page=x` argument to display a different page, with a limit of 20 assets per page.
|
||||||
|
type CatalogAssetsReq = FastifyRequest<{Querystring: { page: string } }>
|
||||||
|
app.get("/api/catalog-assets/", async (request: CatalogAssetsReq, reply) => {
|
||||||
|
try {
|
||||||
|
const { page } = request.query;
|
||||||
|
const pageNum: number = parseInt(page, 10) || 1;
|
||||||
|
if (pageNum < 1) {
|
||||||
|
reply.status(400).send({ error: "Page must be a positive number!" });
|
||||||
|
}
|
||||||
|
const offset = (pageNum - 1) * 20;
|
||||||
|
const totalItems = await catalogAssets.count();
|
||||||
|
const dbAssets = await catalogAssets.findAll({ offset: offset, limit: 20 });
|
||||||
|
const assets = dbAssets.reduce((acc, asset) => {
|
||||||
|
acc[asset.package_name] = {
|
||||||
|
title: asset.title,
|
||||||
|
description: asset.description,
|
||||||
|
author: asset.author,
|
||||||
|
image: asset.image,
|
||||||
|
tags: asset.tags,
|
||||||
|
version: asset.version,
|
||||||
|
background_image: asset.background_image,
|
||||||
|
background_video: asset.background_video,
|
||||||
|
payload: asset.payload,
|
||||||
|
type: asset.type
|
||||||
|
};
|
||||||
|
return acc;
|
||||||
|
}, {});
|
||||||
|
reply.send({ assets, pages: Math.ceil(totalItems / 20) });
|
||||||
|
}
|
||||||
|
catch (error) {
|
||||||
|
reply.status(500).send({ error: 'An error occured' });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
type PackageReq = FastifyRequest<{Params: { package: string } }>
|
||||||
|
app.get("/api/packages/:package", async (request: PackageReq, reply) => {
|
||||||
|
try {
|
||||||
|
const packageRow = await catalogAssets.findOne({ where: { package_name: request.params.package }});
|
||||||
|
if (!packageRow) return reply.status(404).send({ error: 'Package not found!' });
|
||||||
|
const details = {
|
||||||
|
title: packageRow.get("title"),
|
||||||
|
description: packageRow.get('description'),
|
||||||
|
image: packageRow.get('image'),
|
||||||
|
author: packageRow.get('author'),
|
||||||
|
tags: packageRow.get('tags'),
|
||||||
|
version: packageRow.get('version'),
|
||||||
|
background_image: packageRow.get('background_image'),
|
||||||
|
background_video: packageRow.get('background_video'),
|
||||||
|
payload: packageRow.get('payload'),
|
||||||
|
type: packageRow.get('type')
|
||||||
|
};
|
||||||
|
reply.send(details);
|
||||||
|
}
|
||||||
|
catch (error) {
|
||||||
|
reply.status(500).send({ error: 'An unexpected error occured' });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
type UploadReq = FastifyRequest<{Headers: { psk: string }}>;
|
||||||
|
type CreateReq = FastifyRequest<{Headers: { psk: string}, Body: { uuid: string, title: string, image: string, author: string, version: string, description: string, tags: object | any, payload: string, background_video: string, background_image: string, type: string }}>;
|
||||||
|
async function verifyReq(request: UploadReq | CreateReq, reply: FastifyReply, upload: Boolean, data: any) {
|
||||||
|
return new Promise<void>((resolve, reject) => {
|
||||||
|
if (parsedDoc.marketplace.enabled === false) {
|
||||||
|
reply.status(500).send({ error: 'Marketplace is disabled!' });
|
||||||
|
reject();
|
||||||
|
}
|
||||||
|
else if (request.headers.psk !== parsedDoc.marketplace.psk) {
|
||||||
|
reply.status(403).send({ error: 'PSK not correct!' });
|
||||||
|
reject();
|
||||||
|
}
|
||||||
|
else if (upload && !data) {
|
||||||
|
reply.status(400).send({ error: 'No file uploaded!' });
|
||||||
|
reject();
|
||||||
|
}
|
||||||
|
else { resolve(); }
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
app.post("/api/upload-image", async (request: UploadReq, reply) => {
|
||||||
|
const data = await request.file();
|
||||||
|
verifyReq(request, reply, true, data)
|
||||||
|
.then(async () => {
|
||||||
|
await pipeline(data.file, createWriteStream(fileURLToPath(new URL(`../database_assets/image/${data.filename}`, import.meta.url))));
|
||||||
|
reply.send({ message: 'File uploaded successfully!', filename: data.filename });
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
if (parsedDoc.server.server.logging) { console.error(chalk.yellow.bold('Caught error from verify function. \n Error caused while uploading image')) };
|
||||||
|
});
|
||||||
|
});
|
||||||
|
app.post("/api/upload-video", async (request: UploadReq, reply) => {
|
||||||
|
const data = await request.file();
|
||||||
|
verifyReq(request, reply, true, data)
|
||||||
|
.then(async () => {
|
||||||
|
await pipeline(data.file, createWriteStream(fileURLToPath(new URL(`../database_assets/video/${data.filename}`, import.meta.url))));
|
||||||
|
reply.send({ message: 'File uploaded successfully!', filename: data.filename });
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
if (parsedDoc.server.server.logging) { console.error(chalk.yellow.bold('Caught error from verify function. \n Error caused while uploading video')) };
|
||||||
|
});
|
||||||
|
});
|
||||||
|
app.post("/api/upload-style", async (request: UploadReq, reply) => {
|
||||||
|
const data = await request.file();
|
||||||
|
verifyReq(request, reply, true, data)
|
||||||
|
.then(async () => {
|
||||||
|
await pipeline(data.file, createWriteStream(fileURLToPath(new URL(`../database_assets/styles/${data.filename}`, import.meta.url))));
|
||||||
|
reply.send({ message: 'File uploaded successfully!', filename: data.filename });
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
if (parsedDoc.server.server.logging) { console.error(chalk.yellow.bold('Caught error from verify function. \n Error caused while uploading style')) };
|
||||||
|
});
|
||||||
|
});
|
||||||
|
app.post("/api/upload-script", async (request: UploadReq, reply) => {
|
||||||
|
const data = await request.file();
|
||||||
|
verifyReq(request, reply, true, data)
|
||||||
|
.then(async () => {
|
||||||
|
await pipeline(data.file, createWriteStream(fileURLToPath(new URL(`../database_assets/video/${data.filename}`, import.meta.url))));
|
||||||
|
reply.send({ message: 'File uploaded successfully!', filename: data.filename });
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
if (parsedDoc.server.server.logging) { console.error(chalk.yellow.bold('Caught error from verify function. \n Error caused while uploading script')) };
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
app.post("/api/create-package", async (request: CreateReq, reply) => {
|
||||||
|
verifyReq(request, reply, false, undefined)
|
||||||
|
.then(async () => {
|
||||||
|
await catalogAssets.create({
|
||||||
|
package_name: request.body.uuid,
|
||||||
|
title: request.body.title,
|
||||||
|
image: request.body.image,
|
||||||
|
author: request.body.author,
|
||||||
|
version: request.body.version,
|
||||||
|
description: request.body.description,
|
||||||
|
tags: request.body.tags,
|
||||||
|
payload: request.body.payload,
|
||||||
|
background_video: request.body.background_video,
|
||||||
|
background_image: request.body.background_image,
|
||||||
|
type: request.body.type
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.catch(async () => {
|
||||||
|
if (parsedDoc.server.server.logging) { console.error(chalk.yellow.bold('Caught error from verify function. \n Error caused while creating package')) };
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
app.use(ssrHandler);
|
||||||
|
|
||||||
|
const port: number = parseInt(process.env.PORT as string) || parsedDoc.server.server.port || parseInt('8080');
|
||||||
|
const titleText = `
|
||||||
|
_ _ _ _ ____ _
|
||||||
|
| \\ | | ___| |__ _ _| | __ _ / ___| ___ _ ____ _(_) ___ ___ ___
|
||||||
|
| \\| |/ _ \\ '_ \\| | | | |/ _' | \\___ \\ / _ \\ '__\\ \\ / / |/ __/ _ \\/ __|
|
||||||
|
| |\\ | __/ |_) | |_| | | (_| | ___) | __/ | \\ V /| | (_| __/\\__ \\
|
||||||
|
|_| \\_|\\___|_.__/ \\__,_|_|\\__,_| |____/ \\___|_| \\_/ |_|\\___\\___||___/
|
||||||
|
`
|
||||||
|
const titleColors = {
|
||||||
|
purple: "#7967dd",
|
||||||
|
pink: "#eb6f92"
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
console.log(gradient(Object.values(titleColors)).multiline(titleText as string));
|
||||||
|
app.listen({ port: port, host: '0.0.0.0' }).then(() => {
|
||||||
|
console.log(chalk.hex('#7967dd')(`Server listening on ${chalk.hex('#eb6f92').bold('http://localhost:' + port + '/')}`));
|
||||||
|
console.log(chalk.hex('#7967dd')(`Server also listening on ${chalk.hex('#eb6f92').bold('http://0.0.0.0:' + port + '/')}`));
|
||||||
|
catalogAssets.sync();
|
||||||
|
setupDB(catalogAssets);
|
||||||
|
});
|
||||||
|
|
||||||
|
export { CatalogModel }
|
37
server/serverFactory.ts
Normal file
37
server/serverFactory.ts
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
import { createServer } from 'node:http';
|
||||||
|
import wisp from 'wisp-server-node';
|
||||||
|
import rammerhead from '@rubynetwork/rammerhead';
|
||||||
|
import { FastifyServerFactory, FastifyServerFactoryHandler, RawServerDefault } from 'fastify';
|
||||||
|
import { parsedDoc } from './config.js';
|
||||||
|
|
||||||
|
const rh = rammerhead.createRammerhead({
|
||||||
|
logLevel: parsedDoc.server.server.logging ? 'debug' : 'disabled',
|
||||||
|
reverseProxy: parsedDoc.server.rammerhead.reverseproxy,
|
||||||
|
disableLocalStorageSync: parsedDoc.server.rammerhead.localstorage_sync ? false : true,
|
||||||
|
disableHttp2: parsedDoc.server.rammerhead.http2 ? false : true
|
||||||
|
});
|
||||||
|
|
||||||
|
const serverFactory: FastifyServerFactory = (handler: FastifyServerFactoryHandler): RawServerDefault => {
|
||||||
|
const httpServer = createServer();
|
||||||
|
httpServer.on('request', (req, res) => {
|
||||||
|
if (rammerhead.shouldRouteRh(req)) {
|
||||||
|
rammerhead.routeRhRequest(rh, req, res);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
handler(req, res);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
httpServer.on('upgrade', (req, socket, head) => {
|
||||||
|
if (rammerhead.shouldRouteRh(req)) {
|
||||||
|
rammerhead.routeRhUpgrade(rh, req, socket, head);
|
||||||
|
}
|
||||||
|
else if (parsedDoc.server.server.wisp) {
|
||||||
|
if (req.url?.endsWith('/wisp/')) {
|
||||||
|
wisp.routeRequest(req, socket as any, head);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return httpServer;
|
||||||
|
}
|
||||||
|
|
||||||
|
export { serverFactory };
|
11
server/tsconfig.json
Normal file
11
server/tsconfig.json
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"target": "ES2020",
|
||||||
|
"module": "ESNext",
|
||||||
|
"moduleResolution": "bundler",
|
||||||
|
"noEmit": false,
|
||||||
|
"esModuleInterop": true,
|
||||||
|
"skipLibCheck": true,
|
||||||
|
"paths": {}
|
||||||
|
}
|
||||||
|
}
|
|
@ -13,18 +13,15 @@ export const prerender = true;
|
||||||
<script>
|
<script>
|
||||||
import { initSw, setTransport } from "@utils/registerSW.ts"; //../../utils/registerSW.ts
|
import { initSw, setTransport } from "@utils/registerSW.ts"; //../../utils/registerSW.ts
|
||||||
import { Settings } from "@utils/settings/index";
|
import { Settings } from "@utils/settings/index";
|
||||||
document.addEventListener("astro:page-load", async function () {
|
import { pageLoad } from "@utils/events";
|
||||||
try {
|
pageLoad(async () => {
|
||||||
const iframe = document.getElementById("chango") as HTMLIFrameElement;
|
const iframe = document.getElementById("chango") as HTMLIFrameElement;
|
||||||
if (iframe) {
|
if (iframe) {
|
||||||
initSw().then(() => {
|
initSw().then(() => {
|
||||||
setTransport(localStorage.getItem(Settings.ProxySettings.transport) as string).then(async () => {
|
setTransport(localStorage.getItem(Settings.ProxySettings.transport) as string).then(async () => {
|
||||||
iframe.src = __uv$config!.prefix + __uv$config.encodeUrl!("https://radon.games");
|
iframe.src = __uv$config!.prefix + __uv$config.encodeUrl!("https://radon.games");
|
||||||
});
|
|
||||||
});
|
});
|
||||||
}
|
});
|
||||||
}
|
|
||||||
catch (_) {
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -47,6 +47,7 @@ const t = useTranslations(lang);
|
||||||
</Layout>
|
</Layout>
|
||||||
<script>
|
<script>
|
||||||
import { initSw, setTransport } from "@utils/registerSW.ts"; //../../utils/registerSW.ts
|
import { initSw, setTransport } from "@utils/registerSW.ts"; //../../utils/registerSW.ts
|
||||||
|
import { pageLoad } from "@utils/events";
|
||||||
import { RammerheadEncode } from "@rubynetwork/rammerhead-browser";
|
import { RammerheadEncode } from "@rubynetwork/rammerhead-browser";
|
||||||
import { SupportedSites } from "@utils/siteSupport";
|
import { SupportedSites } from "@utils/siteSupport";
|
||||||
import {
|
import {
|
||||||
|
@ -98,9 +99,7 @@ const t = useTranslations(lang);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
//we need to rerun this on every page load
|
//we need to rerun this on every page load
|
||||||
document.addEventListener("astro:page-load", async function () {
|
pageLoad(async () => {
|
||||||
//wrap this in a try catch as sometimes this element will not be available on the page
|
|
||||||
try {
|
|
||||||
const input = document.getElementById("nebula-input") as HTMLInputElement;
|
const input = document.getElementById("nebula-input") as HTMLInputElement;
|
||||||
const iframe = document.getElementById("neb-iframe") as HTMLIFrameElement;
|
const iframe = document.getElementById("neb-iframe") as HTMLIFrameElement;
|
||||||
const omnibox = document.getElementById("omnibox") as HTMLDivElement;
|
const omnibox = document.getElementById("omnibox") as HTMLDivElement;
|
||||||
|
@ -199,9 +198,5 @@ const t = useTranslations(lang);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
} catch (err) {
|
|
||||||
//we purposely don't return anything
|
|
||||||
//console.debug(err);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -133,6 +133,7 @@ export const prerender = true;
|
||||||
<script>
|
<script>
|
||||||
import { toast } from "@utils/toast.ts";
|
import { toast } from "@utils/toast.ts";
|
||||||
import { settings, Settings as SettingsEnum } from "@utils/settings/index";
|
import { settings, Settings as SettingsEnum } from "@utils/settings/index";
|
||||||
|
import { pageLoad } from "@utils/events";
|
||||||
function setup(proxySelectVal: HTMLSelectElement, openInVal: HTMLSelectElement, searchEngineVal: HTMLSelectElement, wispServerVal: HTMLSelectElement, transportVal: HTMLSelectElement) {
|
function setup(proxySelectVal: HTMLSelectElement, openInVal: HTMLSelectElement, searchEngineVal: HTMLSelectElement, wispServerVal: HTMLSelectElement, transportVal: HTMLSelectElement) {
|
||||||
proxySelectVal.value = localStorage.getItem(SettingsEnum.ProxySettings.proxy) as string;
|
proxySelectVal.value = localStorage.getItem(SettingsEnum.ProxySettings.proxy) as string;
|
||||||
openInVal.value = localStorage.getItem(SettingsEnum.ProxySettings.openIn) as string;
|
openInVal.value = localStorage.getItem(SettingsEnum.ProxySettings.openIn) as string;
|
||||||
|
@ -140,40 +141,37 @@ export const prerender = true;
|
||||||
wispServerVal.value = localStorage.getItem(SettingsEnum.ProxySettings.wispServerURL) as string;
|
wispServerVal.value = localStorage.getItem(SettingsEnum.ProxySettings.wispServerURL) as string;
|
||||||
transportVal.value = localStorage.getItem(SettingsEnum.ProxySettings.transport) as string;
|
transportVal.value = localStorage.getItem(SettingsEnum.ProxySettings.transport) as string;
|
||||||
}
|
}
|
||||||
document.addEventListener("astro:page-load", () => {
|
pageLoad(() => {
|
||||||
try {
|
const proxyButton = document.getElementById("setproxy") as HTMLButtonElement;
|
||||||
const proxyButton = document.getElementById("setproxy") as HTMLButtonElement;
|
const proxySelectVal = document.getElementById('proxy') as HTMLSelectElement;
|
||||||
const proxySelectVal = document.getElementById('proxy') as HTMLSelectElement;
|
const openInButton = document.getElementById('setopenin') as HTMLButtonElement;
|
||||||
const openInButton = document.getElementById('setopenin') as HTMLButtonElement;
|
const openInVal = document.getElementById('openin') as HTMLSelectElement;
|
||||||
const openInVal = document.getElementById('openin') as HTMLSelectElement;
|
const searchEngineButton = document.getElementById('setsearchengine') as HTMLButtonElement;
|
||||||
const searchEngineButton = document.getElementById('setsearchengine') as HTMLButtonElement;
|
const searchEngineVal = document.getElementById('searchengine') as HTMLSelectElement;
|
||||||
const searchEngineVal = document.getElementById('searchengine') as HTMLSelectElement;
|
const wispServerButton = document.getElementById('setwispurl') as HTMLButtonElement;
|
||||||
const wispServerButton = document.getElementById('setwispurl') as HTMLButtonElement;
|
const wispServerVal = document.getElementById('wispurl') as HTMLSelectElement;
|
||||||
const wispServerVal = document.getElementById('wispurl') as HTMLSelectElement;
|
const transportButton = document.getElementById('settransport') as HTMLButtonElement;
|
||||||
const transportButton = document.getElementById('settransport') as HTMLButtonElement;
|
const transportVal = document.getElementById('transport') as HTMLSelectElement;
|
||||||
const transportVal = document.getElementById('transport') as HTMLSelectElement;
|
setup(proxySelectVal, openInVal, searchEngineVal, wispServerVal, transportVal);
|
||||||
setup(proxySelectVal, openInVal, searchEngineVal, wispServerVal, transportVal);
|
proxyButton.addEventListener("click", () => {
|
||||||
proxyButton.addEventListener("click", () => {
|
settings.proxySettings.changeProxy(proxySelectVal.value);
|
||||||
settings.proxySettings.changeProxy(proxySelectVal.value);
|
toast('.proxyMessage');
|
||||||
toast('.proxyMessage');
|
});
|
||||||
});
|
openInButton.addEventListener("click", () => {
|
||||||
openInButton.addEventListener("click", () => {
|
settings.proxySettings.openIn(openInVal.value)
|
||||||
settings.proxySettings.openIn(openInVal.value)
|
toast('.openInMessage');
|
||||||
toast('.openInMessage');
|
});
|
||||||
});
|
searchEngineButton.addEventListener('click', () => {
|
||||||
searchEngineButton.addEventListener('click', () => {
|
settings.proxySettings.setSearchEngine(searchEngineVal.value);
|
||||||
settings.proxySettings.setSearchEngine(searchEngineVal.value);
|
toast('.searchEngineMessage');
|
||||||
toast('.searchEngineMessage');
|
});
|
||||||
});
|
wispServerButton.addEventListener('click', () => {
|
||||||
wispServerButton.addEventListener('click', () => {
|
settings.proxySettings.setWispURL(wispServerVal.value);
|
||||||
settings.proxySettings.setWispURL(wispServerVal.value);
|
toast('.wispUrlMessage');
|
||||||
toast('.wispUrlMessage');
|
});
|
||||||
});
|
transportButton.addEventListener('click', () => {
|
||||||
transportButton.addEventListener('click', () => {
|
settings.proxySettings.setTransport(transportVal.value);
|
||||||
settings.proxySettings.setTransport(transportVal.value);
|
toast('.transportMessage');
|
||||||
toast('.transportMessage');
|
});
|
||||||
});
|
|
||||||
}
|
|
||||||
catch(_) {/* Don't return anything on purpose */}
|
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -56,28 +56,26 @@ export const prerender = true;
|
||||||
<script>
|
<script>
|
||||||
import { toast } from "@utils/toast.ts"; //A helper function so we don't have to run this logic everytime.
|
import { toast } from "@utils/toast.ts"; //A helper function so we don't have to run this logic everytime.
|
||||||
import { settings, Settings as SettingsEnum } from "@utils/settings/index";
|
import { settings, Settings as SettingsEnum } from "@utils/settings/index";
|
||||||
|
import { pageLoad } from "@utils/events";
|
||||||
function setup(cloakValue: HTMLSelectElement, abValue: HTMLSelectElement) {
|
function setup(cloakValue: HTMLSelectElement, abValue: HTMLSelectElement) {
|
||||||
const cloakLocal = localStorage.getItem(SettingsEnum.TabSettings.tabCloak);
|
const cloakLocal = localStorage.getItem(SettingsEnum.TabSettings.tabCloak);
|
||||||
const abCloakLocal = localStorage.getItem(SettingsEnum.TabSettings.abblob);
|
const abCloakLocal = localStorage.getItem(SettingsEnum.TabSettings.abblob);
|
||||||
cloakLocal === "default" ? cloakValue.value = "reset" : cloakValue.value = cloakLocal as string;
|
cloakLocal === "default" ? cloakValue.value = "reset" : cloakValue.value = cloakLocal as string;
|
||||||
abValue.value = abCloakLocal as string || 'a:b';
|
abValue.value = abCloakLocal as string || 'a:b';
|
||||||
}
|
}
|
||||||
document.addEventListener("astro:page-load", function() {
|
pageLoad(() => {
|
||||||
try {
|
const cloakButton = document.getElementById("cloak") as HTMLButtonElement;
|
||||||
const cloakButton = document.getElementById("cloak") as HTMLButtonElement;
|
const cloakValue = document.getElementById("cloakselect") as HTMLSelectElement;
|
||||||
const cloakValue = document.getElementById("cloakselect") as HTMLSelectElement;
|
const abButton = document.getElementById("aboutblankbutton") as HTMLButtonElement;
|
||||||
const abButton = document.getElementById("aboutblankbutton") as HTMLButtonElement;
|
const abValue = document.getElementById("aboutblank") as HTMLSelectElement;
|
||||||
const abValue = document.getElementById("aboutblank") as HTMLSelectElement;
|
setup(cloakValue, abValue);
|
||||||
setup(cloakValue, abValue);
|
cloakButton.addEventListener("click", () => {
|
||||||
cloakButton.addEventListener("click", () => {
|
settings.tabSettings.cloakTab(cloakValue.value);
|
||||||
settings.tabSettings.cloakTab(cloakValue.value);
|
toast('.cloakMessageSuccess');
|
||||||
toast('.cloakMessageSuccess');
|
});
|
||||||
});
|
abButton.addEventListener("click", () => {
|
||||||
abButton.addEventListener("click", () => {
|
toast('.abCloakMessage');
|
||||||
toast('.abCloakMessage');
|
settings.tabSettings.abCloak(abValue.value);
|
||||||
settings.tabSettings.abCloak(abValue.value);
|
});
|
||||||
});
|
|
||||||
}
|
|
||||||
catch (_) { /* We don't want to return anything on purpose */ }
|
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -37,6 +37,7 @@ const assetsJson = await response.json();
|
||||||
<variable-define data-assets={JSON.stringify(assetsJson)} data-name={packageName} data-type={assetsJson.type} />
|
<variable-define data-assets={JSON.stringify(assetsJson)} data-name={packageName} data-type={assetsJson.type} />
|
||||||
</Layout>
|
</Layout>
|
||||||
<script>
|
<script>
|
||||||
|
import { pageLoad } from "@utils/events";
|
||||||
import { settings, Settings, type PackageType } from "@utils/settings/index";
|
import { settings, Settings, type PackageType } from "@utils/settings/index";
|
||||||
let packageName: string;
|
let packageName: string;
|
||||||
let assetsJson: any;
|
let assetsJson: any;
|
||||||
|
@ -51,30 +52,28 @@ const assetsJson = await response.json();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
customElements.define('variable-define', VariableDefiner);
|
customElements.define('variable-define', VariableDefiner);
|
||||||
document.addEventListener('astro:page-load', () => {
|
const fn = () => {
|
||||||
try {
|
const items = JSON.parse(localStorage.getItem(Settings.AppearanceSettings.themes) as string) || [];
|
||||||
const items = JSON.parse(localStorage.getItem(Settings.AppearanceSettings.themes) as string) || [];
|
const itemExists = items.indexOf(packageName) !== -1;
|
||||||
const itemExists = items.indexOf(packageName) !== -1;
|
const installButton = document.getElementById("install") as HTMLButtonElement;
|
||||||
const installButton = document.getElementById("install") as HTMLButtonElement;
|
const uninstallButton = document.getElementById("uninstall") as HTMLButtonElement;
|
||||||
const uninstallButton = document.getElementById("uninstall") as HTMLButtonElement;
|
const payload = assetsJson ? JSON.parse(assetsJson) : undefined;
|
||||||
const payload = assetsJson ? JSON.parse(assetsJson) : undefined;
|
if (itemExists) {
|
||||||
if (itemExists) {
|
uninstallButton.classList.remove("hidden");
|
||||||
uninstallButton.classList.remove("hidden");
|
installButton.classList.add("hidden");
|
||||||
installButton.classList.add("hidden");
|
|
||||||
}
|
|
||||||
installButton.addEventListener("click", () => {
|
|
||||||
settings.marketPlaceSettings.install({theme: {payload: payload.payload, video: payload.background_video, bgImage: payload.background_image}}, packageName, payload.payload).then(() => {
|
|
||||||
installButton.classList.add("hidden");
|
|
||||||
uninstallButton.classList.remove("hidden");
|
|
||||||
})
|
|
||||||
});
|
|
||||||
uninstallButton.addEventListener("click", () => {
|
|
||||||
settings.marketPlaceSettings.uninstall(packageType as PackageType, packageName).then(() => {
|
|
||||||
uninstallButton.classList.add("hidden")
|
|
||||||
installButton.classList.remove("hidden");
|
|
||||||
});
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
catch (err) { /* DEBUGGING ONLY: console.error(err) */ }
|
installButton.addEventListener("click", () => {
|
||||||
});
|
settings.marketPlaceSettings.install({theme: {payload: payload.payload, video: payload.background_video, bgImage: payload.background_image}}, packageName, payload.payload).then(() => {
|
||||||
|
installButton.classList.add("hidden");
|
||||||
|
uninstallButton.classList.remove("hidden");
|
||||||
|
})
|
||||||
|
});
|
||||||
|
uninstallButton.addEventListener("click", () => {
|
||||||
|
settings.marketPlaceSettings.uninstall(packageType as PackageType, packageName).then(() => {
|
||||||
|
uninstallButton.classList.add("hidden")
|
||||||
|
installButton.classList.remove("hidden");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
pageLoad(fn);
|
||||||
</script>
|
</script>
|
||||||
|
|
10
src/utils/events.ts
Normal file
10
src/utils/events.ts
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
function pageLoad(fn: () => void) {
|
||||||
|
document.addEventListener("astro:page-load", () => {
|
||||||
|
try {
|
||||||
|
fn();
|
||||||
|
}
|
||||||
|
catch (err) { /* DEBUGGING ONLY: console.error(err) */ }
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export { pageLoad };
|
16
test.js
Normal file
16
test.js
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
import fs from "fs";
|
||||||
|
// This is a test file to upload files to the Nebula server
|
||||||
|
import { File, FormData } from "formdata-node";
|
||||||
|
import { fileFromPath } from "formdata-node/file-from-path";
|
||||||
|
import { parsedDoc } from "./server/config.js";
|
||||||
|
const form = new FormData();
|
||||||
|
const file = new File(["My hovercraft is full of eels"], "example.txt");
|
||||||
|
form.set("file", file);
|
||||||
|
console.log(form);
|
||||||
|
await fetch("http://localhost:8080/api/upload-image", {
|
||||||
|
headers: {
|
||||||
|
PSK: parsedDoc.marketplace.psk
|
||||||
|
},
|
||||||
|
method: "post",
|
||||||
|
body: form
|
||||||
|
}).then(async (res) => { console.log(await res.text()) });
|
Loading…
Add table
Add a link
Reference in a new issue