Compare commits
4 Commits
feat/get-l
...
fix/checkl
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
80aa6aa2aa | ||
|
|
6852e83349 | ||
|
|
8b6e46fe63 | ||
|
|
4088f9abd5 |
@@ -11,10 +11,10 @@ https://kan.bn/api/v1
|
||||
|
||||
## Authentication
|
||||
|
||||
Most endpoints require authentication using your API key. You can create one in the [settings page](https://kan.bn/settings) of your account. Include this key as a Bearer token in the `Authorization` header of each request.
|
||||
Most endpoints require authentication using your API key. You can create one in the [settings page](https://kan.bn/settings) of your account. Include this key in the `x-api-key` header of each request.
|
||||
|
||||
```
|
||||
'Authorization': 'Bearer kan_123456789'
|
||||
'x-api-key': kan_123456789
|
||||
```
|
||||
|
||||
## Response codes
|
||||
|
||||
@@ -28,7 +28,6 @@ const config = {
|
||||
],
|
||||
},
|
||||
|
||||
|
||||
/** Enables hot reloading for local packages without a build step */
|
||||
transpilePackages: [
|
||||
"@kan/api",
|
||||
@@ -66,8 +65,6 @@ const config = {
|
||||
},
|
||||
},
|
||||
},
|
||||
serverExternalPackages: ["pino"],
|
||||
|
||||
experimental: {
|
||||
// instrumentationHook: true,
|
||||
swcPlugins: [["@lingui/swc-plugin", {}]],
|
||||
|
||||
@@ -39,7 +39,7 @@ export function LabelForm({
|
||||
labelPublicId: entityId,
|
||||
},
|
||||
{
|
||||
enabled: !!isEdit && entityId.length >= 12,
|
||||
enabled: isEdit && !!entityId,
|
||||
},
|
||||
);
|
||||
|
||||
|
||||
@@ -280,7 +280,7 @@
|
||||
"message": "Add details...",
|
||||
"placeholders": {},
|
||||
"comments": [],
|
||||
"origin": [["src/views/card/components/ChecklistItemRow.tsx", 169]],
|
||||
"origin": [["src/views/card/components/ChecklistItemRow.tsx", 200]],
|
||||
"translation": "Details hinzufügen..."
|
||||
},
|
||||
"lyqwgn": {
|
||||
@@ -4336,8 +4336,8 @@
|
||||
["src/views/boards/components/ImportBoardsForm.tsx", 243],
|
||||
["src/views/boards/components/ImportBoardsForm.tsx", 395],
|
||||
["src/views/card/components/AttachmentThumbnails.tsx", 74],
|
||||
["src/views/card/components/ChecklistItemRow.tsx", 67],
|
||||
["src/views/card/components/ChecklistItemRow.tsx", 95],
|
||||
["src/views/card/components/ChecklistItemRow.tsx", 69],
|
||||
["src/views/card/components/ChecklistItemRow.tsx", 97],
|
||||
["src/views/card/components/ChecklistNameInput.tsx", 47],
|
||||
["src/views/card/components/Checklists.tsx", 81],
|
||||
["src/views/card/components/Comment.tsx", 93],
|
||||
@@ -5634,7 +5634,7 @@
|
||||
"message": "Unable to delete checklist item",
|
||||
"placeholders": {},
|
||||
"comments": [],
|
||||
"origin": [["src/views/card/components/ChecklistItemRow.tsx", 94]],
|
||||
"origin": [["src/views/card/components/ChecklistItemRow.tsx", 96]],
|
||||
"translation": "Checklistenelement konnte nicht gelöscht werden"
|
||||
},
|
||||
"2QGEbi": {
|
||||
@@ -5718,7 +5718,7 @@
|
||||
"message": "Unable to update checklist item",
|
||||
"placeholders": {},
|
||||
"comments": [],
|
||||
"origin": [["src/views/card/components/ChecklistItemRow.tsx", 66]],
|
||||
"origin": [["src/views/card/components/ChecklistItemRow.tsx", 68]],
|
||||
"translation": "Checklisten-Element konnte nicht aktualisiert werden"
|
||||
},
|
||||
"fYVH36": {
|
||||
|
||||
@@ -471,7 +471,7 @@
|
||||
"origin": [
|
||||
[
|
||||
"src/views/card/components/ChecklistItemRow.tsx",
|
||||
169
|
||||
200
|
||||
]
|
||||
],
|
||||
"translation": "Add details..."
|
||||
@@ -7332,11 +7332,11 @@
|
||||
],
|
||||
[
|
||||
"src/views/card/components/ChecklistItemRow.tsx",
|
||||
67
|
||||
69
|
||||
],
|
||||
[
|
||||
"src/views/card/components/ChecklistItemRow.tsx",
|
||||
95
|
||||
97
|
||||
],
|
||||
[
|
||||
"src/views/card/components/ChecklistNameInput.tsx",
|
||||
@@ -9571,7 +9571,7 @@
|
||||
"origin": [
|
||||
[
|
||||
"src/views/card/components/ChecklistItemRow.tsx",
|
||||
94
|
||||
96
|
||||
]
|
||||
],
|
||||
"translation": "Unable to delete checklist item"
|
||||
@@ -9707,7 +9707,7 @@
|
||||
"origin": [
|
||||
[
|
||||
"src/views/card/components/ChecklistItemRow.tsx",
|
||||
66
|
||||
68
|
||||
]
|
||||
],
|
||||
"translation": "Unable to update checklist item"
|
||||
|
||||
@@ -280,7 +280,7 @@
|
||||
"message": "Add details...",
|
||||
"placeholders": {},
|
||||
"comments": [],
|
||||
"origin": [["src/views/card/components/ChecklistItemRow.tsx", 169]],
|
||||
"origin": [["src/views/card/components/ChecklistItemRow.tsx", 200]],
|
||||
"translation": "Añadir detalles..."
|
||||
},
|
||||
"lyqwgn": {
|
||||
@@ -4336,8 +4336,8 @@
|
||||
["src/views/boards/components/ImportBoardsForm.tsx", 243],
|
||||
["src/views/boards/components/ImportBoardsForm.tsx", 395],
|
||||
["src/views/card/components/AttachmentThumbnails.tsx", 74],
|
||||
["src/views/card/components/ChecklistItemRow.tsx", 67],
|
||||
["src/views/card/components/ChecklistItemRow.tsx", 95],
|
||||
["src/views/card/components/ChecklistItemRow.tsx", 69],
|
||||
["src/views/card/components/ChecklistItemRow.tsx", 97],
|
||||
["src/views/card/components/ChecklistNameInput.tsx", 47],
|
||||
["src/views/card/components/Checklists.tsx", 81],
|
||||
["src/views/card/components/Comment.tsx", 93],
|
||||
@@ -5634,7 +5634,7 @@
|
||||
"message": "Unable to delete checklist item",
|
||||
"placeholders": {},
|
||||
"comments": [],
|
||||
"origin": [["src/views/card/components/ChecklistItemRow.tsx", 94]],
|
||||
"origin": [["src/views/card/components/ChecklistItemRow.tsx", 96]],
|
||||
"translation": "No se pudo eliminar el elemento de la lista de verificación"
|
||||
},
|
||||
"2QGEbi": {
|
||||
@@ -5718,7 +5718,7 @@
|
||||
"message": "Unable to update checklist item",
|
||||
"placeholders": {},
|
||||
"comments": [],
|
||||
"origin": [["src/views/card/components/ChecklistItemRow.tsx", 66]],
|
||||
"origin": [["src/views/card/components/ChecklistItemRow.tsx", 68]],
|
||||
"translation": "No se pudo actualizar el elemento de la lista de verificación"
|
||||
},
|
||||
"fYVH36": {
|
||||
|
||||
@@ -280,7 +280,7 @@
|
||||
"message": "Add details...",
|
||||
"placeholders": {},
|
||||
"comments": [],
|
||||
"origin": [["src/views/card/components/ChecklistItemRow.tsx", 169]],
|
||||
"origin": [["src/views/card/components/ChecklistItemRow.tsx", 200]],
|
||||
"translation": "Ajouter des détails..."
|
||||
},
|
||||
"lyqwgn": {
|
||||
@@ -4336,8 +4336,8 @@
|
||||
["src/views/boards/components/ImportBoardsForm.tsx", 243],
|
||||
["src/views/boards/components/ImportBoardsForm.tsx", 395],
|
||||
["src/views/card/components/AttachmentThumbnails.tsx", 74],
|
||||
["src/views/card/components/ChecklistItemRow.tsx", 67],
|
||||
["src/views/card/components/ChecklistItemRow.tsx", 95],
|
||||
["src/views/card/components/ChecklistItemRow.tsx", 69],
|
||||
["src/views/card/components/ChecklistItemRow.tsx", 97],
|
||||
["src/views/card/components/ChecklistNameInput.tsx", 47],
|
||||
["src/views/card/components/Checklists.tsx", 81],
|
||||
["src/views/card/components/Comment.tsx", 93],
|
||||
@@ -5634,7 +5634,7 @@
|
||||
"message": "Unable to delete checklist item",
|
||||
"placeholders": {},
|
||||
"comments": [],
|
||||
"origin": [["src/views/card/components/ChecklistItemRow.tsx", 94]],
|
||||
"origin": [["src/views/card/components/ChecklistItemRow.tsx", 96]],
|
||||
"translation": "Impossible de supprimer l'élément de liste de contrôle"
|
||||
},
|
||||
"2QGEbi": {
|
||||
@@ -5718,7 +5718,7 @@
|
||||
"message": "Unable to update checklist item",
|
||||
"placeholders": {},
|
||||
"comments": [],
|
||||
"origin": [["src/views/card/components/ChecklistItemRow.tsx", 66]],
|
||||
"origin": [["src/views/card/components/ChecklistItemRow.tsx", 68]],
|
||||
"translation": "Impossible de mettre à jour l'élément de la checklist"
|
||||
},
|
||||
"fYVH36": {
|
||||
|
||||
@@ -280,7 +280,7 @@
|
||||
"message": "Add details...",
|
||||
"placeholders": {},
|
||||
"comments": [],
|
||||
"origin": [["src/views/card/components/ChecklistItemRow.tsx", 169]],
|
||||
"origin": [["src/views/card/components/ChecklistItemRow.tsx", 200]],
|
||||
"translation": "Aggiungi dettagli..."
|
||||
},
|
||||
"lyqwgn": {
|
||||
@@ -4336,8 +4336,8 @@
|
||||
["src/views/boards/components/ImportBoardsForm.tsx", 243],
|
||||
["src/views/boards/components/ImportBoardsForm.tsx", 395],
|
||||
["src/views/card/components/AttachmentThumbnails.tsx", 74],
|
||||
["src/views/card/components/ChecklistItemRow.tsx", 67],
|
||||
["src/views/card/components/ChecklistItemRow.tsx", 95],
|
||||
["src/views/card/components/ChecklistItemRow.tsx", 69],
|
||||
["src/views/card/components/ChecklistItemRow.tsx", 97],
|
||||
["src/views/card/components/ChecklistNameInput.tsx", 47],
|
||||
["src/views/card/components/Checklists.tsx", 81],
|
||||
["src/views/card/components/Comment.tsx", 93],
|
||||
@@ -5634,7 +5634,7 @@
|
||||
"message": "Unable to delete checklist item",
|
||||
"placeholders": {},
|
||||
"comments": [],
|
||||
"origin": [["src/views/card/components/ChecklistItemRow.tsx", 94]],
|
||||
"origin": [["src/views/card/components/ChecklistItemRow.tsx", 96]],
|
||||
"translation": "Impossibile eliminare l'elemento della checklist"
|
||||
},
|
||||
"2QGEbi": {
|
||||
@@ -5718,7 +5718,7 @@
|
||||
"message": "Unable to update checklist item",
|
||||
"placeholders": {},
|
||||
"comments": [],
|
||||
"origin": [["src/views/card/components/ChecklistItemRow.tsx", 66]],
|
||||
"origin": [["src/views/card/components/ChecklistItemRow.tsx", 68]],
|
||||
"translation": "Impossibile aggiornare l'elemento della checklist"
|
||||
},
|
||||
"fYVH36": {
|
||||
|
||||
@@ -280,7 +280,7 @@
|
||||
"message": "Add details...",
|
||||
"placeholders": {},
|
||||
"comments": [],
|
||||
"origin": [["src/views/card/components/ChecklistItemRow.tsx", 169]],
|
||||
"origin": [["src/views/card/components/ChecklistItemRow.tsx", 200]],
|
||||
"translation": "Voeg details toe..."
|
||||
},
|
||||
"lyqwgn": {
|
||||
@@ -4336,8 +4336,8 @@
|
||||
["src/views/boards/components/ImportBoardsForm.tsx", 243],
|
||||
["src/views/boards/components/ImportBoardsForm.tsx", 395],
|
||||
["src/views/card/components/AttachmentThumbnails.tsx", 74],
|
||||
["src/views/card/components/ChecklistItemRow.tsx", 67],
|
||||
["src/views/card/components/ChecklistItemRow.tsx", 95],
|
||||
["src/views/card/components/ChecklistItemRow.tsx", 69],
|
||||
["src/views/card/components/ChecklistItemRow.tsx", 97],
|
||||
["src/views/card/components/ChecklistNameInput.tsx", 47],
|
||||
["src/views/card/components/Checklists.tsx", 81],
|
||||
["src/views/card/components/Comment.tsx", 93],
|
||||
@@ -5634,7 +5634,7 @@
|
||||
"message": "Unable to delete checklist item",
|
||||
"placeholders": {},
|
||||
"comments": [],
|
||||
"origin": [["src/views/card/components/ChecklistItemRow.tsx", 94]],
|
||||
"origin": [["src/views/card/components/ChecklistItemRow.tsx", 96]],
|
||||
"translation": "Kan checklistitem niet verwijderen"
|
||||
},
|
||||
"2QGEbi": {
|
||||
@@ -5718,7 +5718,7 @@
|
||||
"message": "Unable to update checklist item",
|
||||
"placeholders": {},
|
||||
"comments": [],
|
||||
"origin": [["src/views/card/components/ChecklistItemRow.tsx", 66]],
|
||||
"origin": [["src/views/card/components/ChecklistItemRow.tsx", 68]],
|
||||
"translation": "Kan checklistitem niet bijwerken"
|
||||
},
|
||||
"fYVH36": {
|
||||
|
||||
@@ -280,7 +280,7 @@
|
||||
"message": "Add details...",
|
||||
"placeholders": {},
|
||||
"comments": [],
|
||||
"origin": [["src/views/card/components/ChecklistItemRow.tsx", 169]],
|
||||
"origin": [["src/views/card/components/ChecklistItemRow.tsx", 200]],
|
||||
"translation": "Dodaj szczegóły..."
|
||||
},
|
||||
"lyqwgn": {
|
||||
@@ -4336,8 +4336,8 @@
|
||||
["src/views/boards/components/ImportBoardsForm.tsx", 243],
|
||||
["src/views/boards/components/ImportBoardsForm.tsx", 395],
|
||||
["src/views/card/components/AttachmentThumbnails.tsx", 74],
|
||||
["src/views/card/components/ChecklistItemRow.tsx", 67],
|
||||
["src/views/card/components/ChecklistItemRow.tsx", 95],
|
||||
["src/views/card/components/ChecklistItemRow.tsx", 69],
|
||||
["src/views/card/components/ChecklistItemRow.tsx", 97],
|
||||
["src/views/card/components/ChecklistNameInput.tsx", 47],
|
||||
["src/views/card/components/Checklists.tsx", 81],
|
||||
["src/views/card/components/Comment.tsx", 93],
|
||||
@@ -5634,7 +5634,7 @@
|
||||
"message": "Unable to delete checklist item",
|
||||
"placeholders": {},
|
||||
"comments": [],
|
||||
"origin": [["src/views/card/components/ChecklistItemRow.tsx", 94]],
|
||||
"origin": [["src/views/card/components/ChecklistItemRow.tsx", 96]],
|
||||
"translation": "Nie można usunąć elementu listy kontrolnej"
|
||||
},
|
||||
"2QGEbi": {
|
||||
@@ -5718,7 +5718,7 @@
|
||||
"message": "Unable to update checklist item",
|
||||
"placeholders": {},
|
||||
"comments": [],
|
||||
"origin": [["src/views/card/components/ChecklistItemRow.tsx", 66]],
|
||||
"origin": [["src/views/card/components/ChecklistItemRow.tsx", 68]],
|
||||
"translation": "Nie można zaktualizować elementu listy kontrolnej"
|
||||
},
|
||||
"fYVH36": {
|
||||
|
||||
@@ -280,7 +280,7 @@
|
||||
"message": "Add details...",
|
||||
"placeholders": {},
|
||||
"comments": [],
|
||||
"origin": [["src/views/card/components/ChecklistItemRow.tsx", 169]],
|
||||
"origin": [["src/views/card/components/ChecklistItemRow.tsx", 200]],
|
||||
"translation": "Adicionar detalhes..."
|
||||
},
|
||||
"lyqwgn": {
|
||||
@@ -4336,8 +4336,8 @@
|
||||
["src/views/boards/components/ImportBoardsForm.tsx", 243],
|
||||
["src/views/boards/components/ImportBoardsForm.tsx", 395],
|
||||
["src/views/card/components/AttachmentThumbnails.tsx", 74],
|
||||
["src/views/card/components/ChecklistItemRow.tsx", 67],
|
||||
["src/views/card/components/ChecklistItemRow.tsx", 95],
|
||||
["src/views/card/components/ChecklistItemRow.tsx", 69],
|
||||
["src/views/card/components/ChecklistItemRow.tsx", 97],
|
||||
["src/views/card/components/ChecklistNameInput.tsx", 47],
|
||||
["src/views/card/components/Checklists.tsx", 81],
|
||||
["src/views/card/components/Comment.tsx", 93],
|
||||
@@ -5634,7 +5634,7 @@
|
||||
"message": "Unable to delete checklist item",
|
||||
"placeholders": {},
|
||||
"comments": [],
|
||||
"origin": [["src/views/card/components/ChecklistItemRow.tsx", 94]],
|
||||
"origin": [["src/views/card/components/ChecklistItemRow.tsx", 96]],
|
||||
"translation": "Não foi possível excluir item da checklist"
|
||||
},
|
||||
"2QGEbi": {
|
||||
@@ -5718,7 +5718,7 @@
|
||||
"message": "Unable to update checklist item",
|
||||
"placeholders": {},
|
||||
"comments": [],
|
||||
"origin": [["src/views/card/components/ChecklistItemRow.tsx", 66]],
|
||||
"origin": [["src/views/card/components/ChecklistItemRow.tsx", 68]],
|
||||
"translation": "Não foi possível atualizar o item da checklist"
|
||||
},
|
||||
"fYVH36": {
|
||||
|
||||
@@ -280,7 +280,7 @@
|
||||
"message": "Add details...",
|
||||
"placeholders": {},
|
||||
"comments": [],
|
||||
"origin": [["src/views/card/components/ChecklistItemRow.tsx", 169]],
|
||||
"origin": [["src/views/card/components/ChecklistItemRow.tsx", 200]],
|
||||
"translation": "Добавить детали..."
|
||||
},
|
||||
"lyqwgn": {
|
||||
@@ -4336,8 +4336,8 @@
|
||||
["src/views/boards/components/ImportBoardsForm.tsx", 243],
|
||||
["src/views/boards/components/ImportBoardsForm.tsx", 395],
|
||||
["src/views/card/components/AttachmentThumbnails.tsx", 74],
|
||||
["src/views/card/components/ChecklistItemRow.tsx", 67],
|
||||
["src/views/card/components/ChecklistItemRow.tsx", 95],
|
||||
["src/views/card/components/ChecklistItemRow.tsx", 69],
|
||||
["src/views/card/components/ChecklistItemRow.tsx", 97],
|
||||
["src/views/card/components/ChecklistNameInput.tsx", 47],
|
||||
["src/views/card/components/Checklists.tsx", 81],
|
||||
["src/views/card/components/Comment.tsx", 93],
|
||||
@@ -5634,7 +5634,7 @@
|
||||
"message": "Unable to delete checklist item",
|
||||
"placeholders": {},
|
||||
"comments": [],
|
||||
"origin": [["src/views/card/components/ChecklistItemRow.tsx", 94]],
|
||||
"origin": [["src/views/card/components/ChecklistItemRow.tsx", 96]],
|
||||
"translation": "Не удалось удалить элемент контрольного списка"
|
||||
},
|
||||
"2QGEbi": {
|
||||
@@ -5718,7 +5718,7 @@
|
||||
"message": "Unable to update checklist item",
|
||||
"placeholders": {},
|
||||
"comments": [],
|
||||
"origin": [["src/views/card/components/ChecklistItemRow.tsx", 66]],
|
||||
"origin": [["src/views/card/components/ChecklistItemRow.tsx", 68]],
|
||||
"translation": "Не удалось обновить элемент контрольного списка"
|
||||
},
|
||||
"fYVH36": {
|
||||
|
||||
@@ -39,8 +39,6 @@ services:
|
||||
|
||||
# Logging (optional - debug, info, warn, error; defaults to debug in dev, info in prod)
|
||||
- LOG_LEVEL=${LOG_LEVEL}
|
||||
- AXIOM_TOKEN=${AXIOM_TOKEN}
|
||||
- AXIOM_DATASET=${AXIOM_DATASET}
|
||||
|
||||
# Stripe
|
||||
- STRIPE_SECRET_KEY=${STRIPE_SECRET_KEY}
|
||||
|
||||
@@ -227,7 +227,7 @@ export const boardRouter = createTRPCRouter({
|
||||
boardSlug: z
|
||||
.string()
|
||||
.min(3)
|
||||
.max(60)
|
||||
.max(24)
|
||||
.regex(/^(?![-]+$)[a-zA-Z0-9-]+$/),
|
||||
members: z.array(z.string().min(12)).optional(),
|
||||
labels: z.array(z.string().min(12)).optional(),
|
||||
@@ -657,7 +657,7 @@ export const boardRouter = createTRPCRouter({
|
||||
boardSlug: z
|
||||
.string()
|
||||
.min(3)
|
||||
.max(60)
|
||||
.max(24)
|
||||
.regex(/^(?![-]+$)[a-zA-Z0-9-]+$/),
|
||||
boardPublicId: z.string().min(12),
|
||||
}),
|
||||
|
||||
@@ -19,7 +19,6 @@ import { encryptToken } from "../utils/encryption";
|
||||
export const integrationRouter = createTRPCRouter({
|
||||
saveGitHubToken: protectedProcedure
|
||||
.input(z.object({ token: z.string() }))
|
||||
.output(z.object({ success: z.boolean() }))
|
||||
.mutation(async ({ ctx, input }) => {
|
||||
const user = ctx.user;
|
||||
|
||||
@@ -44,9 +43,7 @@ export const integrationRouter = createTRPCRouter({
|
||||
return { success: true };
|
||||
}),
|
||||
|
||||
disconnectGitHub: protectedProcedure
|
||||
.output(z.object({ success: z.boolean() }))
|
||||
.mutation(async ({ ctx }) => {
|
||||
disconnectGitHub: protectedProcedure.mutation(async ({ ctx }) => {
|
||||
const user = ctx.user;
|
||||
|
||||
if (!user)
|
||||
@@ -59,9 +56,7 @@ export const integrationRouter = createTRPCRouter({
|
||||
return { success: true };
|
||||
}),
|
||||
|
||||
getGitHubStatus: protectedProcedure
|
||||
.output(z.object({ connected: z.boolean() }))
|
||||
.query(async ({ ctx }) => {
|
||||
getGitHubStatus: protectedProcedure.query(async ({ ctx }) => {
|
||||
const user = ctx.user;
|
||||
|
||||
if (!user)
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { randomUUID } from "crypto";
|
||||
import type { CreateNextContextOptions } from "@trpc/server/adapters/next";
|
||||
import type { NextApiRequest } from "next";
|
||||
import type { OpenApiMeta } from "trpc-to-openapi";
|
||||
@@ -12,28 +11,7 @@ import { initAuth } from "@kan/auth/server";
|
||||
import { createDrizzleClient } from "@kan/db/client";
|
||||
import { createLogger } from "@kan/logger";
|
||||
|
||||
const log = createLogger("api");
|
||||
|
||||
const TRPC_STATUS_MAP: Partial<Record<TRPCError["code"], number>> = {
|
||||
PARSE_ERROR: 400,
|
||||
BAD_REQUEST: 400,
|
||||
UNAUTHORIZED: 401,
|
||||
FORBIDDEN: 403,
|
||||
NOT_FOUND: 404,
|
||||
METHOD_NOT_SUPPORTED: 405,
|
||||
TIMEOUT: 408,
|
||||
CONFLICT: 409,
|
||||
PRECONDITION_FAILED: 412,
|
||||
PAYLOAD_TOO_LARGE: 413,
|
||||
UNPROCESSABLE_CONTENT: 422,
|
||||
TOO_MANY_REQUESTS: 429,
|
||||
CLIENT_CLOSED_REQUEST: 499,
|
||||
INTERNAL_SERVER_ERROR: 500,
|
||||
NOT_IMPLEMENTED: 501,
|
||||
BAD_GATEWAY: 502,
|
||||
SERVICE_UNAVAILABLE: 503,
|
||||
GATEWAY_TIMEOUT: 504,
|
||||
};
|
||||
const log = createLogger("trpc");
|
||||
|
||||
export interface User {
|
||||
id: string;
|
||||
@@ -72,7 +50,6 @@ interface CreateContextOptions {
|
||||
db: dbClient;
|
||||
auth: ReturnType<typeof createAuthWithHeaders>;
|
||||
headers: Headers;
|
||||
transport?: "trpc" | "rest";
|
||||
}
|
||||
|
||||
export const createInnerTRPCContext = (opts: CreateContextOptions) => {
|
||||
@@ -81,8 +58,6 @@ export const createInnerTRPCContext = (opts: CreateContextOptions) => {
|
||||
db: opts.db,
|
||||
auth: opts.auth,
|
||||
headers: opts.headers,
|
||||
transport: opts.transport ?? "trpc",
|
||||
requestId: randomUUID(),
|
||||
};
|
||||
};
|
||||
|
||||
@@ -94,13 +69,7 @@ export const createTRPCContext = async ({ req }: CreateNextContextOptions) => {
|
||||
|
||||
const session = await auth.api.getSession();
|
||||
|
||||
return createInnerTRPCContext({
|
||||
db,
|
||||
user: session?.user,
|
||||
auth,
|
||||
headers,
|
||||
transport: "trpc",
|
||||
});
|
||||
return createInnerTRPCContext({ db, user: session?.user, auth, headers });
|
||||
};
|
||||
|
||||
export const createNextApiContext = async (req: NextApiRequest) => {
|
||||
@@ -111,13 +80,7 @@ export const createNextApiContext = async (req: NextApiRequest) => {
|
||||
|
||||
const session = await auth.api.getSession();
|
||||
|
||||
return createInnerTRPCContext({
|
||||
db,
|
||||
user: session?.user,
|
||||
auth,
|
||||
headers,
|
||||
transport: "trpc",
|
||||
});
|
||||
return createInnerTRPCContext({ db, user: session?.user, auth, headers });
|
||||
};
|
||||
|
||||
export const createRESTContext = async ({ req }: CreateNextContextOptions) => {
|
||||
@@ -130,16 +93,11 @@ export const createRESTContext = async ({ req }: CreateNextContextOptions) => {
|
||||
try {
|
||||
session = await auth.api.getSession();
|
||||
} catch (error) {
|
||||
log.warn({ err: error }, "Failed to get session, treating as unauthenticated");
|
||||
log.error({ err: error }, "Error getting session");
|
||||
throw error;
|
||||
}
|
||||
|
||||
return createInnerTRPCContext({
|
||||
db,
|
||||
user: session?.user,
|
||||
auth,
|
||||
headers,
|
||||
transport: "rest",
|
||||
});
|
||||
return createInnerTRPCContext({ db, user: session?.user, auth, headers });
|
||||
};
|
||||
|
||||
const t = initTRPC
|
||||
@@ -168,39 +126,20 @@ const loggingMiddleware = t.middleware(async ({ path, type, next, ctx }) => {
|
||||
const result = await next();
|
||||
const duration = Date.now() - start;
|
||||
|
||||
const { user, transport, requestId } = ctx as {
|
||||
user?: { id: string; email: string };
|
||||
transport?: string;
|
||||
requestId?: string;
|
||||
};
|
||||
const isCloud = process.env.NEXT_PUBLIC_KAN_ENV === "cloud";
|
||||
const meta = {
|
||||
requestId,
|
||||
procedure: path,
|
||||
type,
|
||||
transport,
|
||||
duration,
|
||||
userId: user?.id,
|
||||
...(isCloud && { email: user?.email }),
|
||||
};
|
||||
|
||||
const label = transport === "rest" ? "REST" : "tRPC";
|
||||
const meta = { procedure: path, type, duration, userId: (ctx as { user?: { id: string } }).user?.id };
|
||||
|
||||
if (result.ok) {
|
||||
log.info({ ...meta, status: 200 }, `${label} OK`);
|
||||
log.info(meta, "tRPC OK");
|
||||
} else {
|
||||
const status = TRPC_STATUS_MAP[result.error.code] ?? 500;
|
||||
const errorCode = result.error.code;
|
||||
log.error(
|
||||
{ ...meta, status, errorCode, err: result.error },
|
||||
`${label} error`,
|
||||
);
|
||||
log.error({ ...meta, err: result.error }, "tRPC error");
|
||||
}
|
||||
|
||||
return result;
|
||||
});
|
||||
|
||||
export const publicProcedure = t.procedure.use(loggingMiddleware);
|
||||
export const publicProcedure = t.procedure.use(loggingMiddleware).meta({
|
||||
openapi: { method: "GET", path: "/public" },
|
||||
});
|
||||
|
||||
const enforceUserIsAuthed = t.middleware(async ({ ctx, next }) => {
|
||||
if (!ctx.user) {
|
||||
@@ -224,7 +163,13 @@ const enforceUserIsAdmin = t.middleware(async ({ ctx, next }) => {
|
||||
|
||||
export const protectedProcedure = t.procedure
|
||||
.use(loggingMiddleware)
|
||||
.use(enforceUserIsAuthed);
|
||||
.use(enforceUserIsAuthed)
|
||||
.meta({
|
||||
openapi: {
|
||||
method: "GET",
|
||||
path: "/protected",
|
||||
},
|
||||
});
|
||||
|
||||
export const adminProtectedProcedure = t.procedure
|
||||
.use(loggingMiddleware)
|
||||
|
||||
@@ -165,13 +165,6 @@ export function createPlugins(db: dbClient) {
|
||||
: []),
|
||||
apiKey({
|
||||
enableSessionForAPIKeys: true,
|
||||
customAPIKeyGetter: (ctx) => {
|
||||
const authorization = ctx.headers?.get("authorization");
|
||||
if (authorization?.startsWith("Bearer ")) {
|
||||
return authorization.slice(7);
|
||||
}
|
||||
return ctx.headers?.get("x-api-key") ?? undefined;
|
||||
},
|
||||
rateLimit: {
|
||||
enabled: true,
|
||||
timeWindow: 1000 * 60, // 1 minute
|
||||
|
||||
@@ -19,7 +19,6 @@
|
||||
"typecheck": "tsc --noEmit --emitDeclarationOnly false"
|
||||
},
|
||||
"dependencies": {
|
||||
"@axiomhq/js": "^1.4.0",
|
||||
"pino": "^9.14.0",
|
||||
"pino-pretty": "^13.1.3"
|
||||
},
|
||||
|
||||
@@ -1,43 +1,20 @@
|
||||
import { Axiom } from "@axiomhq/js";
|
||||
import pino from "pino";
|
||||
|
||||
const isDev = process.env.NODE_ENV !== "production";
|
||||
const isCloud = process.env.NEXT_PUBLIC_KAN_ENV === "cloud";
|
||||
const level = process.env.LOG_LEVEL || (isDev ? "debug" : "info");
|
||||
|
||||
const axiomToken = process.env.AXIOM_TOKEN;
|
||||
const axiomDataset = process.env.AXIOM_DATASET;
|
||||
const useAxiom = isCloud && !!axiomToken && !!axiomDataset;
|
||||
|
||||
function createAxiomStream(token: string, dataset: string): pino.DestinationStream {
|
||||
const client = new Axiom({ token });
|
||||
return {
|
||||
write(msg: string) {
|
||||
try {
|
||||
client.ingest(dataset, [JSON.parse(msg) as Record<string, unknown>]);
|
||||
} catch {
|
||||
// ignore malformed log lines
|
||||
}
|
||||
export const logger = pino({
|
||||
level,
|
||||
...(isDev && {
|
||||
transport: {
|
||||
target: "pino-pretty",
|
||||
options: {
|
||||
colorize: true,
|
||||
ignore: "pid,hostname",
|
||||
translateTime: "HH:MM:ss",
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export const logger = useAxiom
|
||||
? pino(
|
||||
{ level },
|
||||
pino.multistream([
|
||||
{ stream: process.stdout, level },
|
||||
{ stream: createAxiomStream(axiomToken, axiomDataset), level },
|
||||
]),
|
||||
)
|
||||
: pino({
|
||||
level,
|
||||
...(isDev && {
|
||||
transport: {
|
||||
target: "pino-pretty",
|
||||
options: { colorize: true, ignore: "pid,hostname", translateTime: "HH:MM:ss" },
|
||||
},
|
||||
}),
|
||||
});
|
||||
}),
|
||||
});
|
||||
|
||||
export const createLogger = (module: string) => logger.child({ module });
|
||||
|
||||
16
pnpm-lock.yaml
generated
16
pnpm-lock.yaml
generated
@@ -504,9 +504,6 @@ importers:
|
||||
|
||||
packages/logger:
|
||||
dependencies:
|
||||
'@axiomhq/js':
|
||||
specifier: ^1.4.0
|
||||
version: 1.4.0
|
||||
pino:
|
||||
specifier: ^9.14.0
|
||||
version: 9.14.0
|
||||
@@ -894,10 +891,6 @@ packages:
|
||||
resolution: {integrity: sha512-kLO7k7cGJ6KaHiExSJWojZurF7SnGMDHXRuQunFnEoD0n1yB6Lqy/S/zHiQ7oJnBhPr9q0TW9qFkrsZb1Uc54w==}
|
||||
engines: {node: '>=18.0.0'}
|
||||
|
||||
'@axiomhq/js@1.4.0':
|
||||
resolution: {integrity: sha512-wC5x1ud/QJMstrjpicATkyY8+ZVWEl4WlXMtA5EZf7hkj0+b191yv4yynLxLEfr/MveXora9m6CWdJq4DsbcAg==}
|
||||
engines: {node: '>=20'}
|
||||
|
||||
'@babel/code-frame@7.27.1':
|
||||
resolution: {integrity: sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==}
|
||||
engines: {node: '>=6.9.0'}
|
||||
@@ -5635,9 +5628,6 @@ packages:
|
||||
picomatch:
|
||||
optional: true
|
||||
|
||||
fetch-retry@6.0.0:
|
||||
resolution: {integrity: sha512-BUFj1aMubgib37I3v4q78fYo63Po7t4HUPTpQ6/QE6yK6cIQrP+W43FYToeTEyg5m2Y7eFUtijUuAv/PDlWuag==}
|
||||
|
||||
fflate@0.4.8:
|
||||
resolution: {integrity: sha512-FJqqoDBR00Mdj9ppamLa/Y7vxm+PRmNWA67N846RvsoYVMKB4q3y/de5PA7gUmRMYK/8CMz2GDZQmCRN1wBcWA==}
|
||||
|
||||
@@ -9394,10 +9384,6 @@ snapshots:
|
||||
'@smithy/types': 4.3.2
|
||||
tslib: 2.8.1
|
||||
|
||||
'@axiomhq/js@1.4.0':
|
||||
dependencies:
|
||||
fetch-retry: 6.0.0
|
||||
|
||||
'@babel/code-frame@7.27.1':
|
||||
dependencies:
|
||||
'@babel/helper-validator-identifier': 7.27.1
|
||||
@@ -14417,8 +14403,6 @@ snapshots:
|
||||
optionalDependencies:
|
||||
picomatch: 4.0.3
|
||||
|
||||
fetch-retry@6.0.0: {}
|
||||
|
||||
fflate@0.4.8: {}
|
||||
|
||||
figures@3.2.0:
|
||||
|
||||
@@ -130,9 +130,7 @@
|
||||
"NOVU_API_KEY",
|
||||
"EMAIL_UNSUBSCRIBE_SECRET",
|
||||
"REDIS_URL",
|
||||
"LOG_LEVEL",
|
||||
"AXIOM_TOKEN",
|
||||
"AXIOM_DATASET"
|
||||
"LOG_LEVEL"
|
||||
],
|
||||
"globalPassThroughEnv": [
|
||||
"NODE_ENV",
|
||||
|
||||
Reference in New Issue
Block a user