fixed build
This commit is contained in:
parent
de8ebff8bb
commit
2d71de685e
|
|
@ -1,71 +0,0 @@
|
||||||
# For most projects, this workflow file will not need changing; you simply need
|
|
||||||
# to commit it to your repository.
|
|
||||||
#
|
|
||||||
# You may wish to alter this file to override the set of languages analyzed,
|
|
||||||
# or to provide custom queries or build logic.
|
|
||||||
#
|
|
||||||
# ******** NOTE ********
|
|
||||||
# We have attempted to detect the languages in your repository. Please check
|
|
||||||
# the `language` matrix defined below to confirm you have the correct set of
|
|
||||||
# supported CodeQL languages.
|
|
||||||
#
|
|
||||||
name: 'CodeQL'
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches: [main, 0.x]
|
|
||||||
pull_request:
|
|
||||||
# The branches below must be a subset of the branches above
|
|
||||||
branches: [main]
|
|
||||||
schedule:
|
|
||||||
- cron: '27 0 * * 0'
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
analyze:
|
|
||||||
name: Analyze
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
permissions:
|
|
||||||
actions: read
|
|
||||||
contents: read
|
|
||||||
security-events: write
|
|
||||||
|
|
||||||
strategy:
|
|
||||||
fail-fast: false
|
|
||||||
matrix:
|
|
||||||
language: ['typescript']
|
|
||||||
# CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ]
|
|
||||||
# Learn more:
|
|
||||||
# https://docs.github.com/en/free-pro-team@latest/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#changing-the-languages-that-are-analyzed
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: Checkout repository
|
|
||||||
uses: actions/checkout@v2
|
|
||||||
|
|
||||||
# Initializes the CodeQL tools for scanning.
|
|
||||||
- name: Initialize CodeQL
|
|
||||||
uses: github/codeql-action/init@v1
|
|
||||||
with:
|
|
||||||
languages: ${{ matrix.language }}
|
|
||||||
# If you wish to specify custom queries, you can do so here or in a config file.
|
|
||||||
# By default, queries listed here will override any specified in a config file.
|
|
||||||
# Prefix the list here with "+" to use these queries and those in the config file.
|
|
||||||
# queries: ./path/to/local/query, your-org/your-repo/queries@main
|
|
||||||
|
|
||||||
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
|
|
||||||
# If this step fails, then you should remove it and run the build manually (see below)
|
|
||||||
- name: Autobuild
|
|
||||||
uses: github/codeql-action/autobuild@v1
|
|
||||||
|
|
||||||
# ℹ️ Command-line programs to run using the OS shell.
|
|
||||||
# 📚 https://git.io/JvXDl
|
|
||||||
|
|
||||||
# ✏️ If the Autobuild fails above, remove it and uncomment the following three lines
|
|
||||||
# and modify them (or add more) to build your code if your project
|
|
||||||
# uses a compiled language
|
|
||||||
|
|
||||||
#- run: |
|
|
||||||
# make bootstrap
|
|
||||||
# make release
|
|
||||||
|
|
||||||
- name: Perform CodeQL Analysis
|
|
||||||
uses: github/codeql-action/analyze@v1
|
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
"use strict";
|
||||||
|
|
@ -0,0 +1,4 @@
|
||||||
|
"use strict";
|
||||||
|
Object.defineProperty(exports, "__esModule", { value: true });
|
||||||
|
const react_1 = require("@chakra-ui/react");
|
||||||
|
const theme = (0, react_1.extendTheme)({});
|
||||||
|
|
@ -0,0 +1,26 @@
|
||||||
|
"use strict";
|
||||||
|
Object.defineProperty(exports, "__esModule", { value: true });
|
||||||
|
const react_1 = require("@chakra-ui/react");
|
||||||
|
const CountInput = (props) => {
|
||||||
|
const { getInputProps, getIncrementButtonProps, getDecrementButtonProps } = (0, react_1.useNumberInput)({
|
||||||
|
step: 1,
|
||||||
|
value: props.value,
|
||||||
|
min: 0,
|
||||||
|
onChange: (valueString) => {
|
||||||
|
const value = parseFloat(valueString);
|
||||||
|
if (isNaN(value)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
props.onChange(value);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const inc = getIncrementButtonProps();
|
||||||
|
const dec = getDecrementButtonProps();
|
||||||
|
const input = getInputProps();
|
||||||
|
return (<react_1.HStack maxW="320px">
|
||||||
|
<react_1.Button {...dec}>-</react_1.Button>
|
||||||
|
<react_1.Input {...input}/>
|
||||||
|
<react_1.Button {...inc}>+</react_1.Button>
|
||||||
|
</react_1.HStack>);
|
||||||
|
};
|
||||||
|
exports.default = CountInput;
|
||||||
|
|
@ -0,0 +1,34 @@
|
||||||
|
"use strict";
|
||||||
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
||||||
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
||||||
|
};
|
||||||
|
Object.defineProperty(exports, "__esModule", { value: true });
|
||||||
|
exports.AdminPanel = void 0;
|
||||||
|
const react_1 = require("@chakra-ui/react");
|
||||||
|
const react_2 = require("react");
|
||||||
|
const trpc_1 = require("utils/trpc");
|
||||||
|
const CountInput_1 = __importDefault(require("./components/CountInput"));
|
||||||
|
const AdminPanel = () => {
|
||||||
|
const [gameState, setGameState] = (0, react_2.useState)(undefined);
|
||||||
|
const gameStateQuery = trpc_1.trpc.game.get.useQuery();
|
||||||
|
const mutation = trpc_1.trpc.game.update.useMutation();
|
||||||
|
trpc_1.trpc.game.onUpdate.useSubscription(undefined, {
|
||||||
|
onData: (data) => {
|
||||||
|
setGameState(data);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
(0, react_2.useEffect)(() => {
|
||||||
|
if (gameStateQuery.data) {
|
||||||
|
setGameState(gameStateQuery.data);
|
||||||
|
}
|
||||||
|
}, [gameStateQuery.data]);
|
||||||
|
if (!gameState)
|
||||||
|
return null;
|
||||||
|
return (<react_1.HStack alignItems="start">
|
||||||
|
<react_1.Heading>Gameshow Admin Panel</react_1.Heading>
|
||||||
|
<CountInput_1.default value={gameState.round} onChange={(value) => {
|
||||||
|
mutation.mutate({ ...gameState, round: value });
|
||||||
|
}}/>
|
||||||
|
</react_1.HStack>);
|
||||||
|
};
|
||||||
|
exports.AdminPanel = AdminPanel;
|
||||||
|
|
@ -0,0 +1,10 @@
|
||||||
|
"use strict";
|
||||||
|
Object.defineProperty(exports, "__esModule", { value: true });
|
||||||
|
const react_1 = require("@chakra-ui/react");
|
||||||
|
const trpc_1 = require("utils/trpc");
|
||||||
|
const MyApp = ({ Component, pageProps }) => {
|
||||||
|
return (<react_1.ChakraProvider>
|
||||||
|
<Component {...pageProps}/>
|
||||||
|
</react_1.ChakraProvider>);
|
||||||
|
};
|
||||||
|
exports.default = trpc_1.trpc.withTRPC(MyApp);
|
||||||
|
|
@ -0,0 +1,7 @@
|
||||||
|
"use strict";
|
||||||
|
Object.defineProperty(exports, "__esModule", { value: true });
|
||||||
|
const AdminPanel_1 = require("client/view/AdminPanel");
|
||||||
|
const AdminPage = () => {
|
||||||
|
return <AdminPanel_1.AdminPanel />;
|
||||||
|
};
|
||||||
|
exports.default = AdminPage;
|
||||||
|
|
@ -0,0 +1,53 @@
|
||||||
|
"use strict";
|
||||||
|
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
||||||
|
if (k2 === undefined) k2 = k;
|
||||||
|
var desc = Object.getOwnPropertyDescriptor(m, k);
|
||||||
|
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
||||||
|
desc = { enumerable: true, get: function() { return m[k]; } };
|
||||||
|
}
|
||||||
|
Object.defineProperty(o, k2, desc);
|
||||||
|
}) : (function(o, m, k, k2) {
|
||||||
|
if (k2 === undefined) k2 = k;
|
||||||
|
o[k2] = m[k];
|
||||||
|
}));
|
||||||
|
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
||||||
|
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
||||||
|
}) : function(o, v) {
|
||||||
|
o["default"] = v;
|
||||||
|
});
|
||||||
|
var __importStar = (this && this.__importStar) || function (mod) {
|
||||||
|
if (mod && mod.__esModule) return mod;
|
||||||
|
var result = {};
|
||||||
|
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
||||||
|
__setModuleDefault(result, mod);
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
Object.defineProperty(exports, "__esModule", { value: true });
|
||||||
|
/**
|
||||||
|
* This file contains the tRPC http response handler and context creation for Next.js
|
||||||
|
*/
|
||||||
|
const trpcNext = __importStar(require("@trpc/server/adapters/next"));
|
||||||
|
const context_1 = require("server/context");
|
||||||
|
const _app_1 = require("server/routers/_app");
|
||||||
|
exports.default = trpcNext.createNextApiHandler({
|
||||||
|
router: _app_1.appRouter,
|
||||||
|
/**
|
||||||
|
* @link https://trpc.io/docs/context
|
||||||
|
*/
|
||||||
|
createContext: context_1.createContext,
|
||||||
|
/**
|
||||||
|
* @link https://trpc.io/docs/error-handling
|
||||||
|
*/
|
||||||
|
onError({ error }) {
|
||||||
|
if (error.code === 'INTERNAL_SERVER_ERROR') {
|
||||||
|
// send to bug reporting
|
||||||
|
console.error('Something went wrong', error);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* Enable query batching
|
||||||
|
*/
|
||||||
|
batching: {
|
||||||
|
enabled: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
@ -0,0 +1,9 @@
|
||||||
|
"use strict";
|
||||||
|
Object.defineProperty(exports, "__esModule", { value: true });
|
||||||
|
const react_1 = require("@chakra-ui/react");
|
||||||
|
const IndexPage = () => {
|
||||||
|
return (<>
|
||||||
|
<react_1.Button>Test</react_1.Button>
|
||||||
|
</>);
|
||||||
|
};
|
||||||
|
exports.default = IndexPage;
|
||||||
|
|
@ -0,0 +1,11 @@
|
||||||
|
"use strict";
|
||||||
|
Object.defineProperty(exports, "__esModule", { value: true });
|
||||||
|
exports.createContext = void 0;
|
||||||
|
/**
|
||||||
|
* Creates context for an incoming request
|
||||||
|
* @link https://trpc.io/docs/context
|
||||||
|
*/
|
||||||
|
const createContext = async (opts) => {
|
||||||
|
return {};
|
||||||
|
};
|
||||||
|
exports.createContext = createContext;
|
||||||
|
|
@ -0,0 +1,24 @@
|
||||||
|
"use strict";
|
||||||
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
||||||
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
||||||
|
};
|
||||||
|
Object.defineProperty(exports, "__esModule", { value: true });
|
||||||
|
exports.setGameState = exports.getGameState = void 0;
|
||||||
|
const fs_extra_1 = __importDefault(require("fs-extra"));
|
||||||
|
const initialState = {
|
||||||
|
round: 0,
|
||||||
|
players: [],
|
||||||
|
};
|
||||||
|
const getGameState = () => {
|
||||||
|
try {
|
||||||
|
return fs_extra_1.default.readJSONSync('gameState.json');
|
||||||
|
}
|
||||||
|
catch (_) {
|
||||||
|
return initialState;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
exports.getGameState = getGameState;
|
||||||
|
const setGameState = (state) => {
|
||||||
|
return fs_extra_1.default.writeJSONSync('gameState.json', state);
|
||||||
|
};
|
||||||
|
exports.setGameState = setGameState;
|
||||||
|
|
@ -0,0 +1,42 @@
|
||||||
|
"use strict";
|
||||||
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
||||||
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
||||||
|
};
|
||||||
|
Object.defineProperty(exports, "__esModule", { value: true });
|
||||||
|
const context_1 = require("./context");
|
||||||
|
const _app_1 = require("./routers/_app");
|
||||||
|
const ws_1 = require("@trpc/server/adapters/ws");
|
||||||
|
const http_1 = __importDefault(require("http"));
|
||||||
|
const next_1 = __importDefault(require("next"));
|
||||||
|
const url_1 = require("url");
|
||||||
|
const ws_2 = __importDefault(require("ws"));
|
||||||
|
const port = parseInt(process.env.PORT || '3000', 10);
|
||||||
|
const dev = process.env.NODE_ENV !== 'production';
|
||||||
|
const app = (0, next_1.default)({ dev });
|
||||||
|
const handle = app.getRequestHandler();
|
||||||
|
void app.prepare().then(() => {
|
||||||
|
const server = http_1.default.createServer((req, res) => {
|
||||||
|
var _a;
|
||||||
|
const proto = req.headers['x-forwarded-proto'];
|
||||||
|
if (proto && proto === 'http') {
|
||||||
|
// redirect to ssl
|
||||||
|
res.writeHead(303, {
|
||||||
|
location: `https://` + req.headers.host + ((_a = req.headers.url) !== null && _a !== void 0 ? _a : ''),
|
||||||
|
});
|
||||||
|
res.end();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||||
|
const parsedUrl = (0, url_1.parse)(req.url, true);
|
||||||
|
void handle(req, res, parsedUrl);
|
||||||
|
});
|
||||||
|
const wss = new ws_2.default.Server({ server });
|
||||||
|
const handler = (0, ws_1.applyWSSHandler)({ wss, router: _app_1.appRouter, createContext: context_1.createContext });
|
||||||
|
process.on('SIGTERM', () => {
|
||||||
|
console.log('SIGTERM');
|
||||||
|
handler.broadcastReconnectNotification();
|
||||||
|
});
|
||||||
|
server.listen(port);
|
||||||
|
// tslint:disable-next-line:no-console
|
||||||
|
console.log(`> Server listening at http://localhost:${port} as ${dev ? 'development' : process.env.NODE_ENV}`);
|
||||||
|
});
|
||||||
|
|
@ -0,0 +1,9 @@
|
||||||
|
"use strict";
|
||||||
|
Object.defineProperty(exports, "__esModule", { value: true });
|
||||||
|
exports.appRouter = void 0;
|
||||||
|
const trpc_1 = require("../trpc");
|
||||||
|
const game_1 = require("./game");
|
||||||
|
exports.appRouter = (0, trpc_1.router)({
|
||||||
|
healthcheck: trpc_1.publicProcedure.query(() => 'yay!'),
|
||||||
|
game: game_1.gameRouter,
|
||||||
|
});
|
||||||
|
|
@ -0,0 +1,27 @@
|
||||||
|
"use strict";
|
||||||
|
Object.defineProperty(exports, "__esModule", { value: true });
|
||||||
|
exports.gameRouter = void 0;
|
||||||
|
const events_1 = require("events");
|
||||||
|
const trpc_1 = require("../trpc");
|
||||||
|
const GameState_1 = require("types/GameState");
|
||||||
|
const observable_1 = require("@trpc/server/observable");
|
||||||
|
const gameStateStore_1 = require("server/gameStateStore");
|
||||||
|
class GameEventEmitter extends events_1.EventEmitter {
|
||||||
|
}
|
||||||
|
const gameEventEmitter = new GameEventEmitter();
|
||||||
|
exports.gameRouter = (0, trpc_1.router)({
|
||||||
|
update: trpc_1.publicProcedure.input(GameState_1.gameStateSchema).mutation(({ input }) => {
|
||||||
|
(0, gameStateStore_1.setGameState)(input);
|
||||||
|
gameEventEmitter.emit('update', input);
|
||||||
|
}),
|
||||||
|
get: trpc_1.publicProcedure.query(() => {
|
||||||
|
return (0, gameStateStore_1.getGameState)();
|
||||||
|
}),
|
||||||
|
onUpdate: trpc_1.publicProcedure.subscription(() => {
|
||||||
|
return (0, observable_1.observable)((emit) => {
|
||||||
|
const onUpdate = (state) => emit.next(state);
|
||||||
|
gameEventEmitter.on('update', onUpdate);
|
||||||
|
return () => gameEventEmitter.off('update', onUpdate);
|
||||||
|
});
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
@ -0,0 +1,18 @@
|
||||||
|
"use strict";
|
||||||
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
||||||
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
||||||
|
};
|
||||||
|
Object.defineProperty(exports, "__esModule", { value: true });
|
||||||
|
exports.mergeRouters = exports.middleware = exports.publicProcedure = exports.router = void 0;
|
||||||
|
const server_1 = require("@trpc/server");
|
||||||
|
const superjson_1 = __importDefault(require("superjson"));
|
||||||
|
const t = server_1.initTRPC.context().create({
|
||||||
|
transformer: superjson_1.default,
|
||||||
|
errorFormatter({ shape }) {
|
||||||
|
return shape;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
exports.router = t.router;
|
||||||
|
exports.publicProcedure = t.procedure;
|
||||||
|
exports.middleware = t.middleware;
|
||||||
|
exports.mergeRouters = t.mergeRouters;
|
||||||
|
|
@ -0,0 +1,25 @@
|
||||||
|
"use strict";
|
||||||
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
||||||
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
||||||
|
};
|
||||||
|
Object.defineProperty(exports, "__esModule", { value: true });
|
||||||
|
const context_1 = require("./context");
|
||||||
|
const _app_1 = require("./routers/_app");
|
||||||
|
const ws_1 = require("@trpc/server/adapters/ws");
|
||||||
|
const ws_2 = __importDefault(require("ws"));
|
||||||
|
const wss = new ws_2.default.Server({
|
||||||
|
port: 3001,
|
||||||
|
});
|
||||||
|
const handler = (0, ws_1.applyWSSHandler)({ wss, router: _app_1.appRouter, createContext: context_1.createContext });
|
||||||
|
wss.on('connection', (ws) => {
|
||||||
|
console.log(`➕➕ Connection (${wss.clients.size})`);
|
||||||
|
ws.once('close', () => {
|
||||||
|
console.log(`➖➖ Connection (${wss.clients.size})`);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
console.log('✅ WebSocket Server listening on ws://localhost:3001');
|
||||||
|
process.on('SIGTERM', () => {
|
||||||
|
console.log('SIGTERM');
|
||||||
|
handler.broadcastReconnectNotification();
|
||||||
|
wss.close();
|
||||||
|
});
|
||||||
File diff suppressed because one or more lines are too long
|
|
@ -0,0 +1,13 @@
|
||||||
|
"use strict";
|
||||||
|
Object.defineProperty(exports, "__esModule", { value: true });
|
||||||
|
exports.gameStateSchema = exports.playerSchema = void 0;
|
||||||
|
const zod_1 = require("zod");
|
||||||
|
exports.playerSchema = zod_1.z.object({
|
||||||
|
name: zod_1.z.string(),
|
||||||
|
score: zod_1.z.number(),
|
||||||
|
answer: zod_1.z.string(),
|
||||||
|
});
|
||||||
|
exports.gameStateSchema = zod_1.z.object({
|
||||||
|
round: zod_1.z.number(),
|
||||||
|
players: zod_1.z.array(exports.playerSchema),
|
||||||
|
});
|
||||||
|
|
@ -0,0 +1,78 @@
|
||||||
|
"use strict";
|
||||||
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
||||||
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
||||||
|
};
|
||||||
|
Object.defineProperty(exports, "__esModule", { value: true });
|
||||||
|
exports.trpc = void 0;
|
||||||
|
const httpBatchLink_1 = require("@trpc/client/links/httpBatchLink");
|
||||||
|
const loggerLink_1 = require("@trpc/client/links/loggerLink");
|
||||||
|
const wsLink_1 = require("@trpc/client/links/wsLink");
|
||||||
|
const next_1 = require("@trpc/next");
|
||||||
|
const config_1 = __importDefault(require("next/config"));
|
||||||
|
const superjson_1 = __importDefault(require("superjson"));
|
||||||
|
// ℹ️ Type-only import:
|
||||||
|
// https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-8.html#type-only-imports-and-export
|
||||||
|
const { publicRuntimeConfig } = (0, config_1.default)();
|
||||||
|
const { APP_URL, WS_URL } = publicRuntimeConfig;
|
||||||
|
function getEndingLink(ctx) {
|
||||||
|
if (typeof window === 'undefined') {
|
||||||
|
return (0, httpBatchLink_1.httpBatchLink)({
|
||||||
|
url: `${APP_URL}/api/trpc`,
|
||||||
|
headers() {
|
||||||
|
var _a;
|
||||||
|
if (!((_a = ctx === null || ctx === void 0 ? void 0 : ctx.req) === null || _a === void 0 ? void 0 : _a.headers)) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
// on ssr, forward client's headers to the server
|
||||||
|
return {
|
||||||
|
...ctx.req.headers,
|
||||||
|
'x-ssr': '1',
|
||||||
|
};
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
const client = (0, wsLink_1.createWSClient)({
|
||||||
|
url: WS_URL,
|
||||||
|
});
|
||||||
|
return (0, wsLink_1.wsLink)({
|
||||||
|
client,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* A set of strongly-typed React hooks from your `AppRouter` type signature with `createReactQueryHooks`.
|
||||||
|
* @link https://trpc.io/docs/react#3-create-trpc-hooks
|
||||||
|
*/
|
||||||
|
exports.trpc = (0, next_1.createTRPCNext)({
|
||||||
|
config({ ctx }) {
|
||||||
|
/**
|
||||||
|
* If you want to use SSR, you need to use the server's full URL
|
||||||
|
* @link https://trpc.io/docs/ssr
|
||||||
|
*/
|
||||||
|
return {
|
||||||
|
/**
|
||||||
|
* @link https://trpc.io/docs/client/links
|
||||||
|
*/
|
||||||
|
links: [
|
||||||
|
// adds pretty logs to your console in development and logs errors in production
|
||||||
|
(0, loggerLink_1.loggerLink)({
|
||||||
|
enabled: (opts) => (process.env.NODE_ENV === 'development' &&
|
||||||
|
typeof window !== 'undefined') ||
|
||||||
|
(opts.direction === 'down' && opts.result instanceof Error),
|
||||||
|
}),
|
||||||
|
getEndingLink(ctx),
|
||||||
|
],
|
||||||
|
/**
|
||||||
|
* @link https://trpc.io/docs/data-transformers
|
||||||
|
*/
|
||||||
|
transformer: superjson_1.default,
|
||||||
|
/**
|
||||||
|
* @link https://tanstack.com/query/v4/docs/react/reference/QueryClient
|
||||||
|
*/
|
||||||
|
queryClientConfig: { defaultOptions: { queries: { staleTime: 60 } } },
|
||||||
|
};
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* @link https://trpc.io/docs/ssr
|
||||||
|
*/
|
||||||
|
ssr: true,
|
||||||
|
});
|
||||||
|
|
@ -1,26 +1,9 @@
|
||||||
/**
|
|
||||||
* This file contains the root router of your tRPC-backend
|
|
||||||
*/
|
|
||||||
import { router, publicProcedure } from '../trpc';
|
import { router, publicProcedure } from '../trpc';
|
||||||
import { gameRouter } from './game';
|
import { gameRouter } from './game';
|
||||||
import { postRouter } from './post';
|
|
||||||
import { observable } from '@trpc/server/observable';
|
|
||||||
import { clearInterval } from 'timers';
|
|
||||||
|
|
||||||
export const appRouter = router({
|
export const appRouter = router({
|
||||||
healthcheck: publicProcedure.query(() => 'yay!'),
|
healthcheck: publicProcedure.query(() => 'yay!'),
|
||||||
game: gameRouter,
|
game: gameRouter,
|
||||||
|
|
||||||
randomNumber: publicProcedure.subscription(() => {
|
|
||||||
return observable<number>((emit) => {
|
|
||||||
const int = setInterval(() => {
|
|
||||||
emit.next(Math.random());
|
|
||||||
}, 500);
|
|
||||||
return () => {
|
|
||||||
clearInterval(int);
|
|
||||||
};
|
|
||||||
});
|
|
||||||
}),
|
|
||||||
});
|
});
|
||||||
|
|
||||||
export type AppRouter = typeof appRouter;
|
export type AppRouter = typeof appRouter;
|
||||||
|
|
|
||||||
|
|
@ -1,95 +0,0 @@
|
||||||
/**
|
|
||||||
*
|
|
||||||
* This is an example router, you can delete this file and then update `../pages/api/trpc/[trpc].tsx`
|
|
||||||
*/
|
|
||||||
import { observable } from '@trpc/server/observable';
|
|
||||||
import { EventEmitter } from 'events';
|
|
||||||
import { z } from 'zod';
|
|
||||||
import { authedProcedure, publicProcedure, router } from '../trpc';
|
|
||||||
|
|
||||||
interface MyEvents {
|
|
||||||
add: (data: string) => void;
|
|
||||||
isTypingUpdate: () => void;
|
|
||||||
}
|
|
||||||
declare interface MyEventEmitter {
|
|
||||||
on<TEv extends keyof MyEvents>(event: TEv, listener: MyEvents[TEv]): this;
|
|
||||||
off<TEv extends keyof MyEvents>(event: TEv, listener: MyEvents[TEv]): this;
|
|
||||||
once<TEv extends keyof MyEvents>(event: TEv, listener: MyEvents[TEv]): this;
|
|
||||||
emit<TEv extends keyof MyEvents>(
|
|
||||||
event: TEv,
|
|
||||||
...args: Parameters<MyEvents[TEv]>
|
|
||||||
): boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
class MyEventEmitter extends EventEmitter {}
|
|
||||||
|
|
||||||
// In a real app, you'd probably use Redis or something
|
|
||||||
const ee = new MyEventEmitter();
|
|
||||||
|
|
||||||
// who is currently typing, key is `name`
|
|
||||||
const currentlyTyping: Record<string, { lastTyped: Date }> =
|
|
||||||
Object.create(null);
|
|
||||||
|
|
||||||
// every 1s, clear old "isTyping"
|
|
||||||
const interval = setInterval(() => {
|
|
||||||
let updated = false;
|
|
||||||
const now = Date.now();
|
|
||||||
for (const [key, value] of Object.entries(currentlyTyping)) {
|
|
||||||
if (now - value.lastTyped.getTime() > 3e3) {
|
|
||||||
delete currentlyTyping[key];
|
|
||||||
updated = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (updated) {
|
|
||||||
ee.emit('isTypingUpdate');
|
|
||||||
}
|
|
||||||
}, 3e3);
|
|
||||||
process.on('SIGTERM', () => clearInterval(interval));
|
|
||||||
|
|
||||||
export const postRouter = router({
|
|
||||||
add: authedProcedure
|
|
||||||
.input(
|
|
||||||
z.object({
|
|
||||||
id: z.string().uuid().optional(),
|
|
||||||
text: z.string().min(1),
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
.mutation(async ({ input, ctx }) => {
|
|
||||||
const { name } = ctx.user;
|
|
||||||
ee.emit('add', 'test');
|
|
||||||
delete currentlyTyping[name];
|
|
||||||
ee.emit('isTypingUpdate');
|
|
||||||
}),
|
|
||||||
|
|
||||||
isTyping: authedProcedure
|
|
||||||
.input(z.object({ typing: z.boolean() }))
|
|
||||||
.mutation(({ input, ctx }) => {
|
|
||||||
const { name } = ctx.user;
|
|
||||||
if (!input.typing) {
|
|
||||||
delete currentlyTyping[name];
|
|
||||||
} else {
|
|
||||||
currentlyTyping[name] = {
|
|
||||||
lastTyped: new Date(),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
ee.emit('isTypingUpdate');
|
|
||||||
}),
|
|
||||||
|
|
||||||
whoIsTyping: publicProcedure.subscription(() => {
|
|
||||||
let prev: string[] | null = null;
|
|
||||||
return observable<string[]>((emit) => {
|
|
||||||
const onIsTypingUpdate = () => {
|
|
||||||
const newData = Object.keys(currentlyTyping);
|
|
||||||
|
|
||||||
if (!prev || prev.toString() !== newData.toString()) {
|
|
||||||
emit.next(newData);
|
|
||||||
}
|
|
||||||
prev = newData;
|
|
||||||
};
|
|
||||||
ee.on('isTypingUpdate', onIsTypingUpdate);
|
|
||||||
return () => {
|
|
||||||
ee.off('isTypingUpdate', onIsTypingUpdate);
|
|
||||||
};
|
|
||||||
});
|
|
||||||
}),
|
|
||||||
});
|
|
||||||
|
|
@ -1,70 +1,18 @@
|
||||||
/**
|
|
||||||
* This is your entry point to setup the root configuration for tRPC on the server.
|
|
||||||
* - `initTRPC` should only be used once per app.
|
|
||||||
* - We export only the functionality that we use so we can enforce which base procedures should be used
|
|
||||||
*
|
|
||||||
* Learn how to create protected base procedures and other things below:
|
|
||||||
* @see https://trpc.io/docs/v10/router
|
|
||||||
* @see https://trpc.io/docs/v10/procedures
|
|
||||||
*/
|
|
||||||
|
|
||||||
import { Context } from './context';
|
import { Context } from './context';
|
||||||
import { initTRPC, TRPCError } from '@trpc/server';
|
import { initTRPC } from '@trpc/server';
|
||||||
import superjson from 'superjson';
|
import superjson from 'superjson';
|
||||||
|
|
||||||
const t = initTRPC.context<Context>().create({
|
const t = initTRPC.context<Context>().create({
|
||||||
/**
|
|
||||||
* @see https://trpc.io/docs/v10/data-transformers
|
|
||||||
*/
|
|
||||||
transformer: superjson,
|
transformer: superjson,
|
||||||
/**
|
|
||||||
* @see https://trpc.io/docs/v10/error-formatting
|
|
||||||
*/
|
|
||||||
errorFormatter({ shape }) {
|
errorFormatter({ shape }) {
|
||||||
return shape;
|
return shape;
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a router
|
|
||||||
* @see https://trpc.io/docs/v10/router
|
|
||||||
*/
|
|
||||||
export const router = t.router;
|
export const router = t.router;
|
||||||
|
|
||||||
/**
|
|
||||||
* Create an unprotected procedure
|
|
||||||
* @see https://trpc.io/docs/v10/procedures
|
|
||||||
**/
|
|
||||||
export const publicProcedure = t.procedure;
|
export const publicProcedure = t.procedure;
|
||||||
|
|
||||||
/**
|
|
||||||
* @see https://trpc.io/docs/v10/middlewares
|
|
||||||
*/
|
|
||||||
export const middleware = t.middleware;
|
export const middleware = t.middleware;
|
||||||
|
|
||||||
/**
|
|
||||||
* @see https://trpc.io/docs/v10/merging-routers
|
|
||||||
*/
|
|
||||||
export const mergeRouters = t.mergeRouters;
|
export const mergeRouters = t.mergeRouters;
|
||||||
|
|
||||||
const isAuthed = middleware(({ next, ctx }) => {
|
|
||||||
const user = ctx.session?.user;
|
|
||||||
|
|
||||||
if (!user?.name) {
|
|
||||||
throw new TRPCError({ code: 'UNAUTHORIZED' });
|
|
||||||
}
|
|
||||||
|
|
||||||
return next({
|
|
||||||
ctx: {
|
|
||||||
user: {
|
|
||||||
...user,
|
|
||||||
name: user.name,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Protected base procedure
|
|
||||||
*/
|
|
||||||
export const authedProcedure = t.procedure.use(isAuthed);
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue