diff --git a/docker-compose.yml b/docker-compose.yml index 180c9b2..de9c0fc 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,7 +1,7 @@ version: '3.9' services: app: - container_name: app + container_name: picobook-app image: node build: context: . @@ -16,7 +16,7 @@ services: profiles: ["prod"] db: - container_name: postgres + container_name: picobook-postgres image: postgres env_file: .env diff --git a/package-lock.json b/package-lock.json index 2212f78..f0c4eba 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,13 +11,16 @@ "dependencies": { "@databases/pg": "^5.4.1", "@fastify/cookie": "^9.0.4", + "@fastify/secure-session": "^7.1.0", "@fastify/static": "^6.10.2", "@firebox/components": "^0.1.5", "@firebox/tsutil": "^0.1.2", "@sinclair/typebox": "^0.31.5", - "dotenv": "^16.3.1", + "dotenv": "^16.4.5", "fastify": "^4.22.0", - "react-pico-8": "^4.1.0" + "react-pico-8": "^4.1.0", + "react-router-dom": "^6.18.0", + "uuid": "^9.0.1" }, "devDependencies": { "@databases/pg-migrations": "^5.0.2", @@ -25,6 +28,7 @@ "@emotion/react": "^11.11.1", "@types/react": "^18.2.21", "@types/react-dom": "^18.2.7", + "@types/uuid": "^9.0.7", "esbuild": "^0.19.2", "nodemon": "^3.0.1", "react": "^18.2.0", @@ -956,6 +960,19 @@ "fast-json-stringify": "^5.7.0" } }, + "node_modules/@fastify/secure-session": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/@fastify/secure-session/-/secure-session-7.1.0.tgz", + "integrity": "sha512-x0ZW57+sf6fQaL05B3n64m7DRe81NAtFAKpCKKuqdX9FDqzOH5uQbfDDfVQy2/qhDn3N343RMlNtY1iQyvvA9A==", + "dependencies": { + "@fastify/cookie": "^9.0.4", + "fastify-plugin": "^4.0.0", + "sodium-native": "^4.0.0" + }, + "bin": { + "secure-session": "genkey.js" + } + }, "node_modules/@fastify/send": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/@fastify/send/-/send-2.1.0.tgz", @@ -1139,6 +1156,14 @@ "node": ">=8" } }, + "node_modules/@remix-run/router": { + "version": "1.15.3", + "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.15.3.tgz", + "integrity": "sha512-Oy8rmScVrVxWZVOpEF57ovlnhpZ8CCPlnIIumVcV9nFdiSIrus99+Lw78ekXyGvVDlIsFJbSfmSovJUhCWYV3w==", + "engines": { + "node": ">=14.0.0" + } + }, "node_modules/@sinclair/typebox": { "version": "0.31.5", "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.31.5.tgz", @@ -1217,6 +1242,12 @@ "integrity": "sha512-5cJ8CB4yAx7BH1oMvdU0Jh9lrEXyPkar6F9G/ERswkCuvP4KQZfZkSjcMbAICCpQTN4OuZn8tz0HiKv9TGZgrQ==", "dev": true }, + "node_modules/@types/uuid": { + "version": "9.0.8", + "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-9.0.8.tgz", + "integrity": "sha512-jg+97EGIcY9AGHJJRaaPVgetKDsrTgbRjQ5Msgjh/DQKEFl0DtyRr/VCOyD1T2R1MNeWPK/u7JoGhlDZnKBAfA==", + "dev": true + }, "node_modules/abbrev": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", @@ -1822,14 +1853,14 @@ } }, "node_modules/dotenv": { - "version": "16.3.1", - "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.3.1.tgz", - "integrity": "sha512-IPzF4w4/Rd94bA9imS68tZBaYyBWSCE47V1RGuMrB94iyTOIEwRmVL2x/4An+6mETpLrKJ5hQkB8W4kFAadeIQ==", + "version": "16.4.5", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.5.tgz", + "integrity": "sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==", "engines": { "node": ">=12" }, "funding": { - "url": "https://github.com/motdotla/dotenv?sponsor=1" + "url": "https://dotenvx.com" } }, "node_modules/emoji-regex": { @@ -2983,6 +3014,16 @@ "thenify-all": "^1.0.0" } }, + "node_modules/node-gyp-build": { + "version": "4.8.0", + "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.8.0.tgz", + "integrity": "sha512-u6fs2AEUljNho3EYTJNBfImO5QTo/J/1Etd+NVdCj7qWKUSN/bSLkZwhDv7I+w/MSC6qJ4cknepkAYykDdK8og==", + "bin": { + "node-gyp-build": "bin.js", + "node-gyp-build-optional": "optional.js", + "node-gyp-build-test": "build-test.js" + } + }, "node_modules/nodemon": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-3.0.1.tgz", @@ -3632,6 +3673,36 @@ "react": "^0.13.0 || ^0.14.0 || >=15" } }, + "node_modules/react-router": { + "version": "6.22.3", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.22.3.tgz", + "integrity": "sha512-dr2eb3Mj5zK2YISHK++foM9w4eBnO23eKnZEDs7c880P6oKbrjz/Svg9+nxqtHQK+oMW4OtjZca0RqPglXxguQ==", + "dependencies": { + "@remix-run/router": "1.15.3" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "react": ">=16.8" + } + }, + "node_modules/react-router-dom": { + "version": "6.22.3", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.22.3.tgz", + "integrity": "sha512-7ZILI7HjcE+p31oQvwbokjk6OA/bnFxrhJ19n82Ex9Ph8fNAq+Hm/7KchpMGlTgWhUxRHMMCut+vEtNpWpowKw==", + "dependencies": { + "@remix-run/router": "1.15.3", + "react-router": "6.22.3" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "react": ">=16.8", + "react-dom": ">=16.8" + } + }, "node_modules/readable-stream": { "version": "3.6.2", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", @@ -3883,6 +3954,15 @@ "node": ">=10" } }, + "node_modules/sodium-native": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/sodium-native/-/sodium-native-4.1.1.tgz", + "integrity": "sha512-LXkAfRd4FHtkQS4X6g+nRcVaN7mWVNepV06phIsC6+IZFvGh1voW5TNQiQp2twVaMf05gZqQjuS+uWLM6gHhNQ==", + "hasInstallScript": true, + "dependencies": { + "node-gyp-build": "^4.8.0" + } + }, "node_modules/sonic-boom": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/sonic-boom/-/sonic-boom-3.3.0.tgz", @@ -4186,6 +4266,18 @@ "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", "dev": true }, + "node_modules/uuid": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "bin": { + "uuid": "dist/bin/uuid" + } + }, "node_modules/v8-compile-cache-lib": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", @@ -4985,6 +5077,16 @@ "fast-json-stringify": "^5.7.0" } }, + "@fastify/secure-session": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/@fastify/secure-session/-/secure-session-7.1.0.tgz", + "integrity": "sha512-x0ZW57+sf6fQaL05B3n64m7DRe81NAtFAKpCKKuqdX9FDqzOH5uQbfDDfVQy2/qhDn3N343RMlNtY1iQyvvA9A==", + "requires": { + "@fastify/cookie": "^9.0.4", + "fastify-plugin": "^4.0.0", + "sodium-native": "^4.0.0" + } + }, "@fastify/send": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/@fastify/send/-/send-2.1.0.tgz", @@ -5125,6 +5227,11 @@ "resolved": "https://registry.npmjs.org/@lukeed/ms/-/ms-2.0.1.tgz", "integrity": "sha512-Xs/4RZltsAL7pkvaNStUQt7netTkyxrS0K+RILcVr3TRMS/ToOg4I6uNfhB9SlGsnWBym4U+EaXq0f0cEMNkHA==" }, + "@remix-run/router": { + "version": "1.15.3", + "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.15.3.tgz", + "integrity": "sha512-Oy8rmScVrVxWZVOpEF57ovlnhpZ8CCPlnIIumVcV9nFdiSIrus99+Lw78ekXyGvVDlIsFJbSfmSovJUhCWYV3w==" + }, "@sinclair/typebox": { "version": "0.31.5", "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.31.5.tgz", @@ -5203,6 +5310,12 @@ "integrity": "sha512-5cJ8CB4yAx7BH1oMvdU0Jh9lrEXyPkar6F9G/ERswkCuvP4KQZfZkSjcMbAICCpQTN4OuZn8tz0HiKv9TGZgrQ==", "dev": true }, + "@types/uuid": { + "version": "9.0.8", + "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-9.0.8.tgz", + "integrity": "sha512-jg+97EGIcY9AGHJJRaaPVgetKDsrTgbRjQ5Msgjh/DQKEFl0DtyRr/VCOyD1T2R1MNeWPK/u7JoGhlDZnKBAfA==", + "dev": true + }, "abbrev": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", @@ -5638,9 +5751,9 @@ "dev": true }, "dotenv": { - "version": "16.3.1", - "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.3.1.tgz", - "integrity": "sha512-IPzF4w4/Rd94bA9imS68tZBaYyBWSCE47V1RGuMrB94iyTOIEwRmVL2x/4An+6mETpLrKJ5hQkB8W4kFAadeIQ==" + "version": "16.4.5", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.5.tgz", + "integrity": "sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==" }, "emoji-regex": { "version": "8.0.0", @@ -6498,6 +6611,11 @@ "thenify-all": "^1.0.0" } }, + "node-gyp-build": { + "version": "4.8.0", + "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.8.0.tgz", + "integrity": "sha512-u6fs2AEUljNho3EYTJNBfImO5QTo/J/1Etd+NVdCj7qWKUSN/bSLkZwhDv7I+w/MSC6qJ4cknepkAYykDdK8og==" + }, "nodemon": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-3.0.1.tgz", @@ -6967,6 +7085,23 @@ "integrity": "sha512-/Hi0rH+IJ3ifuoWhPvQkUIwFJjRGcccqoa+MQeB4ceb/u5jRt/hSRS7XD5BsFC79I2VJE1Jh7kyugdDraHFJkg==", "requires": {} }, + "react-router": { + "version": "6.22.3", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.22.3.tgz", + "integrity": "sha512-dr2eb3Mj5zK2YISHK++foM9w4eBnO23eKnZEDs7c880P6oKbrjz/Svg9+nxqtHQK+oMW4OtjZca0RqPglXxguQ==", + "requires": { + "@remix-run/router": "1.15.3" + } + }, + "react-router-dom": { + "version": "6.22.3", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.22.3.tgz", + "integrity": "sha512-7ZILI7HjcE+p31oQvwbokjk6OA/bnFxrhJ19n82Ex9Ph8fNAq+Hm/7KchpMGlTgWhUxRHMMCut+vEtNpWpowKw==", + "requires": { + "@remix-run/router": "1.15.3", + "react-router": "6.22.3" + } + }, "readable-stream": { "version": "3.6.2", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", @@ -7149,6 +7284,14 @@ "semver": "^7.5.3" } }, + "sodium-native": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/sodium-native/-/sodium-native-4.1.1.tgz", + "integrity": "sha512-LXkAfRd4FHtkQS4X6g+nRcVaN7mWVNepV06phIsC6+IZFvGh1voW5TNQiQp2twVaMf05gZqQjuS+uWLM6gHhNQ==", + "requires": { + "node-gyp-build": "^4.8.0" + } + }, "sonic-boom": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/sonic-boom/-/sonic-boom-3.3.0.tgz", @@ -7365,6 +7508,11 @@ "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", "dev": true }, + "uuid": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==" + }, "v8-compile-cache-lib": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", diff --git a/package.json b/package.json index b3e11d9..1bf8b64 100644 --- a/package.json +++ b/package.json @@ -5,16 +5,16 @@ "description": "Firstack is a template repo for a tech stack.", "main": "index.js", "scripts": { + "makeuser": "npm run withenv ./scripts/makeuser.ts", "dev-docker": "docker compose --profile dev up -d", - "dev-server": "echo \"starting server\" && npm run dev-ts ./src/server/index.ts", - "dev-ts": "nodemon --require 'dotenv/config'", - "dev-watch-client": "ts-node ./scripts/watch.ts", + "dev-server": "echo \"starting server\" && npm run withenv ./src/server/index.ts", + "dev-watch-client": "npm run withenv ./scripts/watch.ts", "dev-migrate": "source ./.env && pg-migrations apply --directory ./src/database/migrations", "prod-migrate": "pg-migrations apply --directory ./src/database/migrations", - "prod-build-client": "ts-node ./scripts/build.ts", + "prod-build-client": "npm run withenv ./scripts/build.ts", "prod-docker": "docker compose --profile prod up -d", - "prod-start": "echo \"building frontend\" && npm run prod-build-client && echo \"running migrations\" && npm run prod-migrate && echo \"starting server\" && npm run prod-ts ./src/server/index.ts", - "prod-ts": "ts-node --require 'dotenv/config'", + "prod-start": "echo \"building frontend\" && npm run prod-build-client && echo \"${DATABASE_URL}\" && echo \"running migrations\" && npm run prod-migrate && echo \"starting server\" && npm run withenv ./src/server/index.ts", + "withenv": "tsx ./scripts/run-with-env.ts", "test": "echo \"Error: no test specified\" && exit 1" }, "repository": { @@ -26,12 +26,15 @@ "dependencies": { "@databases/pg": "^5.4.1", "@fastify/cookie": "^9.0.4", + "@fastify/secure-session": "^7.1.0", "@fastify/static": "^6.10.2", "@firebox/components": "^0.1.5", "@firebox/tsutil": "^0.1.2", "@sinclair/typebox": "^0.31.5", - "dotenv": "^16.3.1", + "dotenv": "^16.4.5", "fastify": "^4.22.0", + "react-router-dom": "^6.18.0", + "uuid": "^9.0.1", "react-pico-8": "^4.1.0" }, "devDependencies": { @@ -40,6 +43,7 @@ "@emotion/react": "^11.11.1", "@types/react": "^18.2.21", "@types/react-dom": "^18.2.7", + "@types/uuid": "^9.0.7", "esbuild": "^0.19.2", "nodemon": "^3.0.1", "react": "^18.2.0", diff --git a/scripts/run-with-env.ts b/scripts/run-with-env.ts new file mode 100644 index 0000000..3e5216a --- /dev/null +++ b/scripts/run-with-env.ts @@ -0,0 +1,11 @@ +import dotenv from 'dotenv'; +import * as url from 'url'; +import * as path from 'path'; +const __dirname = url.fileURLToPath(new URL('.', import.meta.url)); +const dotenvPath = path.join(__dirname, "..", ".env"); +dotenv.config({ + path: dotenvPath, +}); +if (process.argv[2]) { + import(path.join(process.cwd(), process.argv[2])); +} \ No newline at end of file diff --git a/src/database/migrations/2-second-migration.sql b/src/database/migrations/2-second-migration.sql index bf2db78..7e9cef2 100644 --- a/src/database/migrations/2-second-migration.sql +++ b/src/database/migrations/2-second-migration.sql @@ -1,8 +1,6 @@ -CREATE TABLE games ( +CREATE TABLE repos ( id text, - name text, -- user defined repo_fullname text, -- e.g. "username/reponame" repo_hosttype text, -- "github", "gitea", "gitlab", ... - repo_token text, -- an api auth token to read from the repo user_id text ) \ No newline at end of file diff --git a/src/database/migrations/3-third-migration.sql b/src/database/migrations/3-third-migration.sql index c9c4181..bf4e508 100644 --- a/src/database/migrations/3-third-migration.sql +++ b/src/database/migrations/3-third-migration.sql @@ -1,7 +1,7 @@ -CREATE TABLE game_instances ( +CREATE TABLE releases ( id text, - game_id text, - cart_data text, - created_at time, - is_official boolean + repo_id text, + release_number integer, + cart_png_base64 text, + created_at time ) \ No newline at end of file diff --git a/src/server/api/release.ts b/src/server/api/release.ts new file mode 100644 index 0000000..172bc28 --- /dev/null +++ b/src/server/api/release.ts @@ -0,0 +1,24 @@ +import { Type } from "@sinclair/typebox"; +import { FirRouteInput, FirRouteOptions } from "../util/routewrap.js"; + +const method = "POST"; +const url = "/release"; + +// const payloadT = Type.Object({ +// png: Type.String(), +// }); + +const payloadT = Type.Any(); + +const handler = ({payload}: FirRouteInput) => { + const {png} = payload; + console.log(png); + // return payload; +}; + +export default { + method, + url, + payloadT, + handler, +} as const satisfies FirRouteOptions; \ No newline at end of file diff --git a/src/server/routelist.ts b/src/server/routelist.ts index fbba4b4..58a6424 100644 --- a/src/server/routelist.ts +++ b/src/server/routelist.ts @@ -1,9 +1,11 @@ import echo from "./api/echo.ts"; +import release from "./api/release.ts"; import webhook from "./api/webhook.ts"; export const routeList = [ echo, webhook, + release, ]; export type RouteList = typeof routeList; \ No newline at end of file