Compare commits
1 Commits
chore/upda
...
feat/impro
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ac9bdaf316 |
@@ -1,62 +1,18 @@
|
||||
# Environment
|
||||
.env
|
||||
.env.*
|
||||
!.env.example
|
||||
|
||||
# Docker
|
||||
docker-compose*.yml
|
||||
docker-compose.override.yml
|
||||
|
||||
Dockerfile
|
||||
./**/*/Dockerfile
|
||||
|
||||
.dockerignore
|
||||
|
||||
# Dependencies (rebuilt in Docker)
|
||||
node_modules
|
||||
**/node_modules
|
||||
./**/*/node_modules
|
||||
|
||||
# Build outputs (rebuilt in Docker)
|
||||
**/.next
|
||||
**/dist
|
||||
**/out
|
||||
**/.turbo
|
||||
**/.cache
|
||||
|
||||
# Git
|
||||
.git
|
||||
.gitignore
|
||||
.gitattributes
|
||||
|
||||
# IDE
|
||||
.vscode
|
||||
.idea
|
||||
*.swp
|
||||
*.swo
|
||||
|
||||
# OS
|
||||
.DS_Store
|
||||
Thumbs.db
|
||||
|
||||
# CI/CD
|
||||
.github
|
||||
.husky
|
||||
.changeset
|
||||
|
||||
# Documentation (not needed in build)
|
||||
*.md
|
||||
!packages/db/migrations/**
|
||||
LICENSE
|
||||
CONTRIBUTING.md
|
||||
AGENTS.md
|
||||
CHANGELOG.md
|
||||
|
||||
# Test files
|
||||
**/*.test.ts
|
||||
**/*.test.tsx
|
||||
**/*.spec.ts
|
||||
**/*.spec.tsx
|
||||
**/__tests__
|
||||
**/coverage
|
||||
|
||||
# Logs
|
||||
pnpm-debug.log
|
||||
**/pnpm-debug.log
|
||||
./**/*/pnpm-debug.log
|
||||
|
||||
# Cloud compose (separate deployment)
|
||||
cloud/
|
||||
README.md
|
||||
.next
|
||||
# .git
|
||||
13
.github/workflows/docker-publish.yml
vendored
13
.github/workflows/docker-publish.yml
vendored
@@ -9,17 +9,11 @@ on:
|
||||
schedule:
|
||||
- cron: "21 21 * * *"
|
||||
push:
|
||||
# Only trigger on tags (releases), not main branch pushes
|
||||
# Main branch builds are handled by workflow_run after Translate completes
|
||||
branches: ["main"]
|
||||
# Publish semver tags as releases.
|
||||
tags: ["v*.*.*"]
|
||||
pull_request:
|
||||
branches: ["main"]
|
||||
workflow_run:
|
||||
workflows: ["Translate"]
|
||||
types: [completed]
|
||||
branches: [main]
|
||||
# Docker builds after Translate completes (success or failure)
|
||||
# This ensures Docker always has the latest translations
|
||||
|
||||
env:
|
||||
# Use docker.io for Docker Hub if empty
|
||||
@@ -29,9 +23,6 @@ env:
|
||||
|
||||
jobs:
|
||||
build:
|
||||
concurrency:
|
||||
group: docker-build-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
49
.github/workflows/translate.yml
vendored
49
.github/workflows/translate.yml
vendored
@@ -1,49 +0,0 @@
|
||||
name: Translate
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
jobs:
|
||||
translate:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Setup Node
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20
|
||||
|
||||
- name: Extract translations
|
||||
working-directory: apps/web
|
||||
run: npx --yes @lingui/cli extract
|
||||
|
||||
- name: Lingo.dev
|
||||
uses: lingodotdev/lingo.dev@main
|
||||
with:
|
||||
api-key: ${{ secrets.LINGODOTDEV_API_KEY }}
|
||||
commit-message: "chore: update translations"
|
||||
working-directory: apps/web
|
||||
commit-author-name: "${{ github.actor }}"
|
||||
commit-author-email: "${{ github.actor }}@users.noreply.github.com"
|
||||
parallel: true
|
||||
|
||||
- name: Compile translations
|
||||
working-directory: apps/web
|
||||
run: npx --yes @lingui/cli compile --typescript
|
||||
|
||||
- name: Commit compiled translations
|
||||
run: |
|
||||
git config --local user.email "${{ github.actor }}@users.noreply.github.com"
|
||||
git config --local user.name "${{ github.actor }}"
|
||||
if ls apps/web/src/locales/*/messages.ts 1> /dev/null 2>&1; then
|
||||
git add apps/web/src/locales/*/messages.ts
|
||||
git diff --staged --quiet || (git commit -m "chore: compile translations" && git push)
|
||||
fi
|
||||
|
||||
@@ -1,23 +1,25 @@
|
||||
# syntax=docker/dockerfile:1.7
|
||||
|
||||
ARG NODE_VERSION=20
|
||||
ARG DISTROLESS_NODE_IMAGE=gcr.io/distroless/nodejs${NODE_VERSION}-debian12
|
||||
ARG APP_DIRNAME=web
|
||||
ARG PROJECT=@kan/web
|
||||
|
||||
# ============================================
|
||||
# Stage 1: Alpine base with pnpm and turbo
|
||||
# ============================================
|
||||
# 1. Alpine image
|
||||
FROM node:${NODE_VERSION}-alpine AS alpine
|
||||
RUN apk update && \
|
||||
apk add --no-cache libc6-compat && \
|
||||
rm -rf /var/cache/apk/* /tmp/* || true && \
|
||||
corepack enable && \
|
||||
npm install turbo@2.3.1 --global && \
|
||||
pnpm config set store-dir ~/.pnpm-store
|
||||
apk add --no-cache --virtual .build-deps libc6-compat python3 make g++ && \
|
||||
rm -rf /var/cache/apk/* /tmp/* || true
|
||||
|
||||
# ============================================
|
||||
# Stage 2: Prune the monorepo
|
||||
# ============================================
|
||||
FROM alpine AS pruner
|
||||
# Setup pnpm and turbo on the alpine base
|
||||
FROM alpine AS base
|
||||
RUN corepack enable
|
||||
# Replace <your-major-version> with the major version installed in your repository. For example:
|
||||
# RUN npm install turbo@2.1.3 --global
|
||||
RUN npm install turbo@2.3.1 --global
|
||||
|
||||
RUN pnpm config set store-dir ~/.pnpm-store
|
||||
|
||||
# 2. Prune projects
|
||||
FROM base AS pruner
|
||||
ARG PROJECT
|
||||
|
||||
RUN apk add --no-cache git
|
||||
|
||||
@@ -33,89 +35,53 @@ RUN git fetch --tags --unshallow 2>/dev/null || git fetch --tags 2>/dev/null ||
|
||||
echo "unknown") && \
|
||||
echo "$AUTO_VERSION" > /app/AUTO_VERSION
|
||||
|
||||
RUN turbo prune --scope=@kan/web --scope=@kan/db --docker
|
||||
RUN turbo prune --scope=${PROJECT} --scope=@kan/db --docker
|
||||
|
||||
# 3. Build the project
|
||||
FROM base AS builder
|
||||
ARG PROJECT
|
||||
ARG APP_VERSION
|
||||
|
||||
# ============================================
|
||||
# Stage 3: Install dependencies
|
||||
# ============================================
|
||||
FROM alpine AS deps
|
||||
WORKDIR /app
|
||||
|
||||
COPY --from=pruner /app/out/pnpm-lock.yaml ./pnpm-lock.yaml
|
||||
COPY --from=pruner /app/out/pnpm-workspace.yaml ./pnpm-workspace.yaml
|
||||
COPY --from=pruner /app/out/json/ .
|
||||
|
||||
ENV CI=true
|
||||
RUN --mount=type=cache,id=pnpm,target=~/.pnpm-store pnpm install --frozen-lockfile
|
||||
|
||||
# ============================================
|
||||
# Stage 4: Build the web application
|
||||
# ============================================
|
||||
FROM alpine AS builder
|
||||
ARG APP_VERSION
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
COPY --from=deps /app/ ./
|
||||
COPY --from=pruner /app/out/full/ .
|
||||
COPY --from=pruner /app/AUTO_VERSION /tmp/AUTO_VERSION
|
||||
|
||||
# Force standalone output for production Docker image
|
||||
ENV NEXT_PUBLIC_USE_STANDALONE_OUTPUT=true
|
||||
ENV CI=true
|
||||
|
||||
RUN --mount=type=cache,id=pnpm,target=~/.pnpm-store pnpm install --frozen-lockfile
|
||||
|
||||
COPY --from=pruner /app/out/full/ .
|
||||
|
||||
# Use provided APP_VERSION or auto-generated from pruner stage
|
||||
RUN VERSION="${APP_VERSION:-$(cat /tmp/AUTO_VERSION 2>/dev/null | tr -d '\n\r' || echo 'unknown')}" && \
|
||||
NEXT_PUBLIC_APP_VERSION="$VERSION" pnpm build --filter=@kan/web
|
||||
NEXT_PUBLIC_APP_VERSION="$VERSION" pnpm build --filter=${PROJECT}
|
||||
|
||||
# ============================================
|
||||
# Stage 5: Migration image (run-once container)
|
||||
# ============================================
|
||||
FROM node:${NODE_VERSION}-alpine AS migrate
|
||||
WORKDIR /db
|
||||
# # Copy static files to standalone directory
|
||||
# RUN mkdir -p apps/web/.next/standalone/.next && \
|
||||
# mv apps/web/public apps/web/.next/standalone/ && \
|
||||
# mv apps/web/.next/static apps/web/.next/standalone/.next/
|
||||
|
||||
COPY packages/db/drizzle.config.ts ./drizzle.config.ts
|
||||
COPY packages/db/migrations/ ./migrations/
|
||||
# 4. Final image - runner stage to run the application
|
||||
FROM base AS runner
|
||||
ARG APP_DIRNAME
|
||||
|
||||
# Don't run production as root
|
||||
RUN addgroup --system --gid 1001 nodejs
|
||||
RUN adduser --system --uid 1001 nextjs
|
||||
USER nextjs
|
||||
|
||||
RUN npm init -y && \
|
||||
npm install drizzle-kit drizzle-orm pg --save-exact && \
|
||||
# Strip unnecessary files from node_modules
|
||||
find node_modules -type f \( \
|
||||
-name '*.d.ts' -o \
|
||||
-name '*.d.mts' -o \
|
||||
-name '*.d.cts' -o \
|
||||
-name '*.map' -o \
|
||||
-name '*.md' -o \
|
||||
-name '*.txt' -o \
|
||||
-name 'LICENSE*' -o \
|
||||
-name 'CHANGELOG*' -o \
|
||||
-name 'README*' -o \
|
||||
-name '.eslint*' -o \
|
||||
-name '.prettier*' -o \
|
||||
-name 'tsconfig*.json' \
|
||||
\) -delete && \
|
||||
find node_modules -type d -empty -delete && \
|
||||
rm -rf /root/.npm /tmp/*
|
||||
|
||||
CMD ["npx", "drizzle-kit", "migrate"]
|
||||
|
||||
# ============================================
|
||||
# Stage 6: Production web image (distroless)
|
||||
# ============================================
|
||||
FROM ${DISTROLESS_NODE_IMAGE} AS web
|
||||
WORKDIR /app
|
||||
|
||||
ENV NODE_ENV=production
|
||||
ENV PORT=3000
|
||||
ENV HOSTNAME=0.0.0.0
|
||||
|
||||
# Copy the standalone Next.js server
|
||||
COPY --from=builder /app/apps/web/.next/standalone/ ./
|
||||
# Copy static assets and public files
|
||||
COPY --from=builder /app/apps/web/.next/static/ ./apps/web/.next/static/
|
||||
COPY --from=builder /app/apps/web/public/ ./apps/web/public/
|
||||
COPY --chown=nextjs:nodejs --from=builder /app/ ./
|
||||
WORKDIR /app/apps/${APP_DIRNAME}
|
||||
|
||||
# Copy bootstrap script for runtime env var injection
|
||||
COPY apps/web/bootstrap.cjs ./bootstrap.cjs
|
||||
ARG PORT=3000
|
||||
ENV PORT=${PORT}
|
||||
EXPOSE ${PORT}
|
||||
|
||||
EXPOSE 3000
|
||||
CMD ["bootstrap.cjs"]
|
||||
CMD ["sh", "-c", "if [ -n \"$POSTGRES_URL\" ]; then cd /app && pnpm db:migrate && cd /app/apps/web; fi && pnpm start"]
|
||||
|
||||
@@ -1,44 +0,0 @@
|
||||
/**
|
||||
* Bootstrap script for the distroless production image.
|
||||
*
|
||||
* Distroless images have no shell, so this Node.js script handles two tasks
|
||||
* that would normally be done in an entrypoint.sh:
|
||||
*
|
||||
* 1. Regenerate `public/__ENV.js` with the current runtime NEXT_PUBLIC_*
|
||||
* environment variables. The file was originally created at build time by
|
||||
* next-runtime-env's `configureRuntimeEnv()`, but in a Docker deployment the
|
||||
* env vars are provided at *run* time via docker-compose / docker run.
|
||||
*
|
||||
* 2. Start the Next.js standalone server.
|
||||
*/
|
||||
|
||||
const { writeFileSync, existsSync, mkdirSync } = require("fs");
|
||||
const path = require("path");
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// 1. Inject runtime NEXT_PUBLIC_* env vars into __ENV.js
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
const publicDir = path.join(__dirname, "apps", "web", "public");
|
||||
|
||||
if (!existsSync(publicDir)) {
|
||||
mkdirSync(publicDir, { recursive: true });
|
||||
}
|
||||
|
||||
const envVars = {};
|
||||
for (const [key, value] of Object.entries(process.env)) {
|
||||
if (key.startsWith("NEXT_PUBLIC_")) {
|
||||
envVars[key] = value;
|
||||
}
|
||||
}
|
||||
|
||||
writeFileSync(
|
||||
path.join(publicDir, "__ENV.js"),
|
||||
`self.__ENV = ${JSON.stringify(envVars)};`,
|
||||
);
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// 2. Start the Next.js standalone server
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
require("./apps/web/server.js");
|
||||
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"version": "1.10",
|
||||
"version": 0,
|
||||
"locale": {
|
||||
"source": "en",
|
||||
"targets": ["fr", "de", "es", "it", "nl", "ru", "pl", "pt-BR"]
|
||||
},
|
||||
"buckets": {
|
||||
"json": {
|
||||
"include": ["src/locales/[locale]/messages.json"]
|
||||
"po": {
|
||||
"include": ["src/locales/[locale]/messages.po"]
|
||||
}
|
||||
},
|
||||
"$schema": "https://lingo.dev/schema/i18n.json"
|
||||
|
||||
3261
apps/web/i18n.lock
3261
apps/web/i18n.lock
File diff suppressed because it is too large
Load Diff
@@ -1,9 +1,7 @@
|
||||
import { defineConfig } from "@lingui/cli";
|
||||
import { formatter } from "@lingui/format-json";
|
||||
import type { LinguiConfig } from "@lingui/conf";
|
||||
|
||||
|
||||
export default defineConfig({
|
||||
locales: ["en", "fr", "de", "es", "it", "nl", "ru", "pl", "pt-BR"],
|
||||
const config: LinguiConfig = {
|
||||
locales: ["en", "fr", "de", "es", "it", "nl", "ru", "pl","pt-BR"],
|
||||
sourceLocale: "en",
|
||||
catalogs: [
|
||||
{
|
||||
@@ -12,5 +10,6 @@ export default defineConfig({
|
||||
exclude: ["**/node_modules/**"],
|
||||
},
|
||||
],
|
||||
format: formatter({ style: "lingui" }),
|
||||
});
|
||||
};
|
||||
|
||||
export default config;
|
||||
|
||||
@@ -16,18 +16,6 @@ const config = {
|
||||
: undefined,
|
||||
reactStrictMode: true,
|
||||
|
||||
/** Exclude build tools and dev-only packages from the standalone output */
|
||||
outputFileTracingExcludes: {
|
||||
"**/*": [
|
||||
"@esbuild/**",
|
||||
"esbuild/**",
|
||||
"typescript/**",
|
||||
"webpack/**",
|
||||
"uglify-js/**",
|
||||
"terser/**",
|
||||
],
|
||||
},
|
||||
|
||||
/** Enables hot reloading for local packages without a build step */
|
||||
transpilePackages: [
|
||||
"@kan/api",
|
||||
@@ -47,13 +35,49 @@ const config = {
|
||||
remotePatterns: (() => {
|
||||
/** @type {Array<{protocol: "http" | "https", hostname: string}>} */
|
||||
const patterns = [
|
||||
{ protocol: "https", hostname: "**" },
|
||||
{
|
||||
protocol: "https",
|
||||
hostname:
|
||||
env("S3_FORCE_PATH_STYLE") === "true"
|
||||
? `${env("NEXT_PUBLIC_STORAGE_DOMAIN")}`
|
||||
: `*.${env("NEXT_PUBLIC_STORAGE_DOMAIN")}`,
|
||||
},
|
||||
{
|
||||
protocol: "http",
|
||||
hostname: "localhost",
|
||||
},
|
||||
{
|
||||
protocol: "https",
|
||||
hostname: "*.googleusercontent.com",
|
||||
},
|
||||
{
|
||||
protocol: 'https',
|
||||
hostname: 'cdn.discordapp.com',
|
||||
},
|
||||
];
|
||||
|
||||
// Extract root domain from S3_ENDPOINT and add wildcard pattern
|
||||
const s3Endpoint = env("S3_ENDPOINT");
|
||||
if (s3Endpoint) {
|
||||
try {
|
||||
const url = new URL(s3Endpoint);
|
||||
const hostname = url.hostname;
|
||||
const protocol = url.protocol.replace(":", "");
|
||||
|
||||
// Extract root domain (last 2 parts: e.g. cloudflarestorage.com)
|
||||
const parts = hostname.split(".");
|
||||
if (parts.length >= 2) {
|
||||
const rootDomain = parts.slice(-2).join(".");
|
||||
patterns.push({
|
||||
protocol: protocol === "http" ? "http" : "https",
|
||||
hostname: `*.${rootDomain}`,
|
||||
});
|
||||
}
|
||||
} catch {
|
||||
// If S3_ENDPOINT is not a valid URL, ignore it
|
||||
}
|
||||
}
|
||||
|
||||
return patterns;
|
||||
})(),
|
||||
},
|
||||
|
||||
@@ -79,7 +79,6 @@
|
||||
"@kan/tailwind-config": "workspace:*",
|
||||
"@kan/tsconfig": "workspace:*",
|
||||
"@lingui/cli": "^5.3.2",
|
||||
"@lingui/format-json": "^5.9.1",
|
||||
"@lingui/loader": "^5.3.2",
|
||||
"@lingui/swc-plugin": "^5.5.2",
|
||||
"@svgr/webpack": "^8.1.0",
|
||||
|
||||
@@ -33,7 +33,7 @@ const schema = z.object({
|
||||
.min(3, {
|
||||
message: t`URL must be at least 3 characters long`,
|
||||
})
|
||||
.max(64, { message: t`URL cannot exceed 64 characters` })
|
||||
.max(24, { message: t`URL cannot exceed 24 characters` })
|
||||
.regex(/^(?![-]+$)[a-zA-Z0-9-]+$/, {
|
||||
message: t`URL can only contain letters, numbers, and hyphens`,
|
||||
})
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
3090
apps/web/src/locales/de/messages.po
Normal file
3090
apps/web/src/locales/de/messages.po
Normal file
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
3275
apps/web/src/locales/en/messages.po
Normal file
3275
apps/web/src/locales/en/messages.po
Normal file
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
3090
apps/web/src/locales/es/messages.po
Normal file
3090
apps/web/src/locales/es/messages.po
Normal file
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
3090
apps/web/src/locales/fr/messages.po
Normal file
3090
apps/web/src/locales/fr/messages.po
Normal file
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
3090
apps/web/src/locales/it/messages.po
Normal file
3090
apps/web/src/locales/it/messages.po
Normal file
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
3090
apps/web/src/locales/nl/messages.po
Normal file
3090
apps/web/src/locales/nl/messages.po
Normal file
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
3095
apps/web/src/locales/pl/messages.po
Normal file
3095
apps/web/src/locales/pl/messages.po
Normal file
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
3090
apps/web/src/locales/pt-BR/messages.po
Normal file
3090
apps/web/src/locales/pt-BR/messages.po
Normal file
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
3090
apps/web/src/locales/ru/messages.po
Normal file
3090
apps/web/src/locales/ru/messages.po
Normal file
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
@@ -11,7 +11,7 @@ import { withRateLimit } from "@kan/api/utils/rateLimit";
|
||||
const workspaceSlugSchema = z
|
||||
.string()
|
||||
.min(3)
|
||||
.max(64)
|
||||
.max(24)
|
||||
.regex(/^(?![-]+$)[a-zA-Z0-9-]+$/);
|
||||
|
||||
interface CheckoutSessionRequest {
|
||||
|
||||
@@ -33,7 +33,7 @@ const UpdateWorkspaceUrlForm = ({
|
||||
.min(3, {
|
||||
message: t`URL must be at least 3 characters long`,
|
||||
})
|
||||
.max(64, { message: t`URL cannot exceed 64 characters` })
|
||||
.max(24, { message: t`URL cannot exceed 24 characters` })
|
||||
.regex(/^(?![-]+$)[a-zA-Z0-9-]+$/, {
|
||||
message: t`URL can only contain letters, numbers, and hyphens`,
|
||||
}),
|
||||
|
||||
@@ -1,17 +1,4 @@
|
||||
services:
|
||||
migrator:
|
||||
image: ghcr.io/kanbn/kan-migrate:latest
|
||||
container_name: ${MIGRATOR_CONTAINER_NAME:-kan-migrate}
|
||||
build:
|
||||
context: ..
|
||||
dockerfile: apps/web/Dockerfile
|
||||
target: migrate
|
||||
networks:
|
||||
- dokploy-network
|
||||
environment:
|
||||
- POSTGRES_URL=${POSTGRES_URL}
|
||||
restart: "no"
|
||||
|
||||
web:
|
||||
image: ghcr.io/kanbn/kan:latest
|
||||
container_name: ${CONTAINER_NAME:-kan-web}
|
||||
@@ -22,7 +9,6 @@ services:
|
||||
build:
|
||||
context: ..
|
||||
dockerfile: apps/web/Dockerfile
|
||||
target: web
|
||||
args:
|
||||
APP_VERSION: ${APP_VERSION:-}
|
||||
env_file:
|
||||
@@ -93,9 +79,6 @@ services:
|
||||
- NEXT_PUBLIC_UMAMI_ID=${NEXT_PUBLIC_UMAMI_ID}
|
||||
- NEXT_PUBLIC_POSTHOG_KEY=${NEXT_PUBLIC_POSTHOG_KEY}
|
||||
- NEXT_PUBLIC_POSTHOG_HOST=${NEXT_PUBLIC_POSTHOG_HOST}
|
||||
depends_on:
|
||||
migrator:
|
||||
condition: service_completed_successfully
|
||||
|
||||
networks:
|
||||
dokploy-network:
|
||||
|
||||
@@ -1,20 +1,4 @@
|
||||
services:
|
||||
migrate:
|
||||
image: ghcr.io/kanbn/kan-migrate:latest
|
||||
container_name: ${CONTAINER_NAME:-kan-migrate}
|
||||
networks:
|
||||
- kan-network
|
||||
build:
|
||||
context: .
|
||||
dockerfile: ./apps/web/Dockerfile
|
||||
target: migrate
|
||||
environment:
|
||||
- POSTGRES_URL=${POSTGRES_URL}
|
||||
depends_on:
|
||||
postgres:
|
||||
condition: service_healthy
|
||||
restart: "no"
|
||||
|
||||
web:
|
||||
image: ghcr.io/kanbn/kan:latest
|
||||
container_name: ${CONTAINER_NAME:-kan-web}
|
||||
@@ -22,10 +6,10 @@ services:
|
||||
- "${WEB_PORT:-3000}:3000"
|
||||
networks:
|
||||
- kan-network
|
||||
- dokploy-network
|
||||
build:
|
||||
context: .
|
||||
dockerfile: ./apps/web/Dockerfile
|
||||
target: web
|
||||
dockerfile: ./apps/web/Dockerfile.new
|
||||
env_file:
|
||||
- .env
|
||||
environment:
|
||||
@@ -123,8 +107,7 @@ services:
|
||||
- APPLE_CLIENT_SECRET=${APPLE_CLIENT_SECRET}
|
||||
- APPLE_APP_BUNDLE_IDENTIFIER=${APPLE_APP_BUNDLE_IDENTIFIER}
|
||||
depends_on:
|
||||
migrate:
|
||||
condition: service_completed_successfully
|
||||
- postgres
|
||||
restart: unless-stopped
|
||||
|
||||
postgres:
|
||||
@@ -138,17 +121,14 @@ services:
|
||||
- 5432:5432
|
||||
volumes:
|
||||
- kan_postgres_data:/var/lib/postgresql/data
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "pg_isready -U kan -d kan_db"]
|
||||
interval: 5s
|
||||
timeout: 5s
|
||||
retries: 10
|
||||
restart: unless-stopped
|
||||
networks:
|
||||
- kan-network
|
||||
|
||||
networks:
|
||||
kan-network:
|
||||
dokploy-network:
|
||||
external: true
|
||||
|
||||
volumes:
|
||||
kan_postgres_data:
|
||||
|
||||
@@ -218,7 +218,7 @@ export const boardRouter = createTRPCRouter({
|
||||
workspaceSlug: z
|
||||
.string()
|
||||
.min(3)
|
||||
.max(64)
|
||||
.max(24)
|
||||
.regex(/^(?![-]+$)[a-zA-Z0-9-]+$/),
|
||||
boardSlug: z
|
||||
.string()
|
||||
|
||||
@@ -160,7 +160,7 @@ export const workspaceRouter = createTRPCRouter({
|
||||
workspaceSlug: z
|
||||
.string()
|
||||
.min(3)
|
||||
.max(64)
|
||||
.max(24)
|
||||
.regex(/^(?![-]+$)[a-zA-Z0-9-]+$/),
|
||||
}),
|
||||
)
|
||||
@@ -207,7 +207,7 @@ export const workspaceRouter = createTRPCRouter({
|
||||
slug: z
|
||||
.string()
|
||||
.min(3)
|
||||
.max(64)
|
||||
.max(24)
|
||||
.regex(/^(?![-]+$)[a-zA-Z0-9-]+$/)
|
||||
.optional(),
|
||||
}),
|
||||
@@ -290,7 +290,7 @@ export const workspaceRouter = createTRPCRouter({
|
||||
slug: z
|
||||
.string()
|
||||
.min(3)
|
||||
.max(64)
|
||||
.max(24)
|
||||
.regex(/^(?![-]+$)[a-zA-Z0-9-]+$/)
|
||||
.optional(),
|
||||
description: z.string().min(3).max(280).optional(),
|
||||
@@ -424,7 +424,7 @@ export const workspaceRouter = createTRPCRouter({
|
||||
workspaceSlug: z
|
||||
.string()
|
||||
.min(3)
|
||||
.max(64)
|
||||
.max(24)
|
||||
.regex(/^(?![-]+$)[a-zA-Z0-9-]+$/),
|
||||
}),
|
||||
)
|
||||
|
||||
@@ -53,7 +53,7 @@ export async function sendMentionEmails({
|
||||
const commenter = await userRepo.getById(db, commenterUserId);
|
||||
if (!commenter) return;
|
||||
|
||||
const commenterName = commenter.name?.trim() || commenter.email;
|
||||
const commenterName = commenter.name ?? commenter.email;
|
||||
|
||||
// Get mentioned members with full details (filtered by workspace)
|
||||
const membersWithDetails = await memberRepo.getByPublicIdsWithUsers(
|
||||
|
||||
@@ -12,13 +12,15 @@ import { configuredProviders } from "./providers";
|
||||
|
||||
export const initAuth = (db: dbClient) => {
|
||||
const baseURL = env("NEXT_PUBLIC_BASE_URL") || env("BETTER_AUTH_URL");
|
||||
const trustedOrigins =
|
||||
env("BETTER_AUTH_TRUSTED_ORIGINS")?.split(",").filter(Boolean) ?? [];
|
||||
const trustedOrigins = env("BETTER_AUTH_TRUSTED_ORIGINS")?.split(",") ?? [];
|
||||
|
||||
return betterAuth({
|
||||
secret: env("BETTER_AUTH_SECRET"),
|
||||
baseURL,
|
||||
trustedOrigins: [...(baseURL ? [baseURL] : []), ...trustedOrigins],
|
||||
trustedOrigins: [
|
||||
...(baseURL ? [baseURL] : []),
|
||||
...trustedOrigins,
|
||||
],
|
||||
database: drizzleAdapter(db, {
|
||||
provider: "pg",
|
||||
schema: {
|
||||
|
||||
@@ -7,8 +7,8 @@ import * as memberRepo from "@kan/db/repository/member.repo";
|
||||
import * as subscriptionRepo from "@kan/db/repository/subscription.repo";
|
||||
import * as userRepo from "@kan/db/repository/user.repo";
|
||||
import * as workspaceRepo from "@kan/db/repository/workspace.repo";
|
||||
import { sendEmail } from "@kan/email";
|
||||
import { generateUID } from "@kan/shared/utils";
|
||||
import { sendEmail } from "@kan/email";
|
||||
import { createStripeClient } from "@kan/stripe";
|
||||
|
||||
import { socialProvidersPlugin } from "./providers";
|
||||
@@ -139,11 +139,10 @@ export function createPlugins(db: dbClient) {
|
||||
let newSlug = workspace.publicId;
|
||||
|
||||
if (workspace.slug !== workspace.publicId) {
|
||||
const isPublicIdAvailable =
|
||||
await workspaceRepo.isWorkspaceSlugAvailable(
|
||||
db,
|
||||
workspace.publicId,
|
||||
);
|
||||
const isPublicIdAvailable = await workspaceRepo.isWorkspaceSlugAvailable(
|
||||
db,
|
||||
workspace.publicId,
|
||||
);
|
||||
if (!isPublicIdAvailable) {
|
||||
newSlug = generateUID();
|
||||
}
|
||||
@@ -173,77 +172,69 @@ export function createPlugins(db: dbClient) {
|
||||
magicLink({
|
||||
expiresIn: 60 * 60 * 24 * 7, // 7 days
|
||||
sendMagicLink: async ({ email, url }) => {
|
||||
try {
|
||||
const decodedUrl = decodeURIComponent(url);
|
||||
console.log("Sending magic link to:", email, "URL:", url);
|
||||
console.log(
|
||||
"Magic link contains invite:",
|
||||
decodedUrl.includes("type=invite"),
|
||||
);
|
||||
if (decodedUrl.includes("type=invite")) {
|
||||
let inviterName = "";
|
||||
let workspaceName = "";
|
||||
const decodedUrl = decodeURIComponent(url);
|
||||
console.log("Sending magic link to:", email, "URL:", url);
|
||||
console.log(
|
||||
"Magic link contains invite:",
|
||||
decodedUrl.includes("type=invite"),
|
||||
);
|
||||
if (decodedUrl.includes("type=invite")) {
|
||||
let inviterName = "";
|
||||
let workspaceName = "";
|
||||
|
||||
try {
|
||||
const urlObj = new URL(url);
|
||||
const callbackUrl = urlObj.searchParams.get("callbackURL");
|
||||
if (callbackUrl) {
|
||||
const callbackParams = new URL(
|
||||
callbackUrl,
|
||||
process.env.NEXT_PUBLIC_BASE_URL,
|
||||
).searchParams;
|
||||
const memberPublicId = callbackParams.get("memberPublicId");
|
||||
try {
|
||||
const urlObj = new URL(url);
|
||||
const callbackUrl = urlObj.searchParams.get("callbackURL");
|
||||
if (callbackUrl) {
|
||||
const callbackParams = new URL(
|
||||
callbackUrl,
|
||||
process.env.NEXT_PUBLIC_BASE_URL,
|
||||
).searchParams;
|
||||
const memberPublicId = callbackParams.get("memberPublicId");
|
||||
|
||||
if (memberPublicId) {
|
||||
const member = await memberRepo.getByPublicId(
|
||||
db,
|
||||
memberPublicId,
|
||||
);
|
||||
if (member) {
|
||||
const [workspace, inviter] = await Promise.all([
|
||||
workspaceRepo.getById(db, member.workspaceId),
|
||||
userRepo.getById(db, member.createdBy),
|
||||
]);
|
||||
if (memberPublicId) {
|
||||
const member = await memberRepo.getByPublicId(
|
||||
db,
|
||||
memberPublicId,
|
||||
);
|
||||
if (member) {
|
||||
const [workspace, inviter] = await Promise.all([
|
||||
workspaceRepo.getById(db, member.workspaceId),
|
||||
userRepo.getById(db, member.createdBy),
|
||||
]);
|
||||
|
||||
if (workspace) workspaceName = workspace.name;
|
||||
if (inviter) inviterName = inviter.name ?? "";
|
||||
}
|
||||
if (workspace) workspaceName = workspace.name;
|
||||
if (inviter) inviterName = inviter.name ?? "";
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Failed to fetch invite details:", error);
|
||||
}
|
||||
|
||||
await sendEmail(
|
||||
email,
|
||||
workspaceName
|
||||
? `Invitation to join the workspace ${workspaceName}`
|
||||
: "Invitation to join workspace",
|
||||
"JOIN_WORKSPACE",
|
||||
{
|
||||
magicLoginUrl: url,
|
||||
inviterName,
|
||||
workspaceName,
|
||||
},
|
||||
);
|
||||
} else {
|
||||
await sendEmail(
|
||||
email,
|
||||
process.env.NEXT_PUBLIC_WHITE_LABEL_HIDE_POWERED_BY === "true"
|
||||
? "Sign in to your account"
|
||||
: "Sign in to Kan",
|
||||
"MAGIC_LINK",
|
||||
{
|
||||
magicLoginUrl: url,
|
||||
},
|
||||
);
|
||||
} catch (error) {
|
||||
console.error("Failed to fetch invite details:", error);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error sending magic link:", {
|
||||
|
||||
await sendEmail(
|
||||
email,
|
||||
url,
|
||||
error,
|
||||
});
|
||||
workspaceName
|
||||
? `Invitation to join the workspace ${workspaceName}`
|
||||
: "Invitation to join workspace",
|
||||
"JOIN_WORKSPACE",
|
||||
{
|
||||
magicLoginUrl: url,
|
||||
inviterName,
|
||||
workspaceName,
|
||||
},
|
||||
);
|
||||
} else {
|
||||
await sendEmail(
|
||||
email,
|
||||
process.env.NEXT_PUBLIC_WHITE_LABEL_HIDE_POWERED_BY === "true"
|
||||
? "Sign in to your account"
|
||||
: "Sign in to Kan",
|
||||
"MAGIC_LINK",
|
||||
{
|
||||
magicLoginUrl: url,
|
||||
},
|
||||
);
|
||||
}
|
||||
},
|
||||
}),
|
||||
|
||||
@@ -1,28 +0,0 @@
|
||||
CREATE TABLE IF NOT EXISTS "workspace_webhooks" (
|
||||
"id" bigserial PRIMARY KEY NOT NULL,
|
||||
"publicId" varchar(12) NOT NULL,
|
||||
"workspaceId" bigint NOT NULL,
|
||||
"name" varchar(255) NOT NULL,
|
||||
"url" varchar(2048) NOT NULL,
|
||||
"secret" text,
|
||||
"events" text NOT NULL,
|
||||
"active" boolean DEFAULT true NOT NULL,
|
||||
"createdBy" uuid NOT NULL,
|
||||
"createdAt" timestamp DEFAULT now() NOT NULL,
|
||||
"updatedAt" timestamp,
|
||||
CONSTRAINT "workspace_webhooks_publicId_unique" UNIQUE("publicId")
|
||||
);
|
||||
--> statement-breakpoint
|
||||
ALTER TABLE "workspace_webhooks" ENABLE ROW LEVEL SECURITY;--> statement-breakpoint
|
||||
CREATE INDEX IF NOT EXISTS "workspace_webhooks_workspace_idx" ON "workspace_webhooks" USING btree ("workspaceId");--> statement-breakpoint
|
||||
DO $$ BEGIN
|
||||
ALTER TABLE "workspace_webhooks" ADD CONSTRAINT "workspace_webhooks_workspaceId_workspace_id_fk" FOREIGN KEY ("workspaceId") REFERENCES "public"."workspace"("id") ON DELETE cascade ON UPDATE no action;
|
||||
EXCEPTION
|
||||
WHEN duplicate_object THEN null;
|
||||
END $$;
|
||||
--> statement-breakpoint
|
||||
DO $$ BEGIN
|
||||
ALTER TABLE "workspace_webhooks" ADD CONSTRAINT "workspace_webhooks_createdBy_user_id_fk" FOREIGN KEY ("createdBy") REFERENCES "public"."user"("id") ON DELETE cascade ON UPDATE no action;
|
||||
EXCEPTION
|
||||
WHEN duplicate_object THEN null;
|
||||
END $$;
|
||||
@@ -190,13 +190,6 @@
|
||||
"when": 1770521594167,
|
||||
"tag": "20260208033314_AddAttachmentToActivity",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 27,
|
||||
"version": "7",
|
||||
"when": 1771192000000,
|
||||
"tag": "20260129210000_AddWorkspaceWebhooks",
|
||||
"breakpoints": true
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -945,14 +945,12 @@ export const getWorkspaceAndCardIdByCardPublicId = async (
|
||||
where: and(eq(cards.publicId, cardPublicId), isNull(cards.deletedAt)),
|
||||
with: {
|
||||
list: {
|
||||
columns: { name: true },
|
||||
columns: {},
|
||||
with: {
|
||||
board: {
|
||||
columns: {
|
||||
publicId: true,
|
||||
workspaceId: true,
|
||||
visibility: true,
|
||||
name: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -966,9 +964,6 @@ export const getWorkspaceAndCardIdByCardPublicId = async (
|
||||
createdBy: result.createdBy,
|
||||
workspaceId: result.list.board.workspaceId,
|
||||
workspaceVisibility: result.list.board.visibility,
|
||||
listName: result.list.name,
|
||||
boardPublicId: result.list.board.publicId,
|
||||
boardName: result.list.board.name,
|
||||
}
|
||||
: null;
|
||||
};
|
||||
|
||||
@@ -419,14 +419,12 @@ export const getWorkspaceAndListIdByListPublicId = async (
|
||||
listPublicId: string,
|
||||
) => {
|
||||
const result = await db.query.lists.findFirst({
|
||||
columns: { id: true, name: true, createdBy: true },
|
||||
columns: { id: true, createdBy: true },
|
||||
where: and(eq(lists.publicId, listPublicId), isNull(lists.deletedAt)),
|
||||
with: {
|
||||
board: {
|
||||
columns: {
|
||||
publicId: true,
|
||||
workspaceId: true,
|
||||
name: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -435,11 +433,8 @@ export const getWorkspaceAndListIdByListPublicId = async (
|
||||
return result
|
||||
? {
|
||||
id: result.id,
|
||||
name: result.name,
|
||||
createdBy: result.createdBy,
|
||||
workspaceId: result.board.workspaceId,
|
||||
boardPublicId: result.board.publicId,
|
||||
boardName: result.board.name,
|
||||
}
|
||||
: null;
|
||||
};
|
||||
|
||||
@@ -1,175 +0,0 @@
|
||||
import { and, eq } from "drizzle-orm";
|
||||
|
||||
import type { dbClient } from "@kan/db/client";
|
||||
import { workspaceWebhooks } from "@kan/db/schema";
|
||||
import { generateUID } from "@kan/shared/utils";
|
||||
|
||||
import type { WebhookEvent } from "../schema/webhooks";
|
||||
|
||||
/** Parse JSON-encoded events column into typed array */
|
||||
function parseEvents(raw: string): WebhookEvent[] {
|
||||
return JSON.parse(raw) as WebhookEvent[];
|
||||
}
|
||||
|
||||
export const create = async (
|
||||
db: dbClient,
|
||||
webhookInput: {
|
||||
workspaceId: number;
|
||||
name: string;
|
||||
url: string;
|
||||
secret?: string;
|
||||
events: WebhookEvent[];
|
||||
createdBy: string;
|
||||
},
|
||||
) => {
|
||||
const [webhook] = await db
|
||||
.insert(workspaceWebhooks)
|
||||
.values({
|
||||
publicId: generateUID(),
|
||||
workspaceId: webhookInput.workspaceId,
|
||||
name: webhookInput.name,
|
||||
url: webhookInput.url,
|
||||
secret: webhookInput.secret,
|
||||
events: JSON.stringify(webhookInput.events),
|
||||
createdBy: webhookInput.createdBy,
|
||||
})
|
||||
.returning({
|
||||
publicId: workspaceWebhooks.publicId,
|
||||
name: workspaceWebhooks.name,
|
||||
url: workspaceWebhooks.url,
|
||||
events: workspaceWebhooks.events,
|
||||
active: workspaceWebhooks.active,
|
||||
createdAt: workspaceWebhooks.createdAt,
|
||||
});
|
||||
|
||||
return webhook
|
||||
? {
|
||||
...webhook,
|
||||
events: parseEvents(webhook.events),
|
||||
}
|
||||
: null;
|
||||
};
|
||||
|
||||
export const update = async (
|
||||
db: dbClient,
|
||||
webhookPublicId: string,
|
||||
webhookInput: {
|
||||
name?: string;
|
||||
url?: string;
|
||||
secret?: string;
|
||||
events?: WebhookEvent[];
|
||||
active?: boolean;
|
||||
},
|
||||
) => {
|
||||
const [result] = await db
|
||||
.update(workspaceWebhooks)
|
||||
.set({
|
||||
name: webhookInput.name,
|
||||
url: webhookInput.url,
|
||||
secret: webhookInput.secret,
|
||||
events: webhookInput.events
|
||||
? JSON.stringify(webhookInput.events)
|
||||
: undefined,
|
||||
active: webhookInput.active,
|
||||
updatedAt: new Date(),
|
||||
})
|
||||
.where(eq(workspaceWebhooks.publicId, webhookPublicId))
|
||||
.returning({
|
||||
publicId: workspaceWebhooks.publicId,
|
||||
name: workspaceWebhooks.name,
|
||||
url: workspaceWebhooks.url,
|
||||
events: workspaceWebhooks.events,
|
||||
active: workspaceWebhooks.active,
|
||||
createdAt: workspaceWebhooks.createdAt,
|
||||
updatedAt: workspaceWebhooks.updatedAt,
|
||||
});
|
||||
|
||||
return result
|
||||
? {
|
||||
...result,
|
||||
events: parseEvents(result.events),
|
||||
}
|
||||
: null;
|
||||
};
|
||||
|
||||
export const getByPublicId = async (db: dbClient, webhookPublicId: string) => {
|
||||
const result = await db.query.workspaceWebhooks.findFirst({
|
||||
columns: {
|
||||
id: true,
|
||||
publicId: true,
|
||||
workspaceId: true,
|
||||
name: true,
|
||||
url: true,
|
||||
secret: true,
|
||||
events: true,
|
||||
active: true,
|
||||
createdAt: true,
|
||||
updatedAt: true,
|
||||
},
|
||||
where: eq(workspaceWebhooks.publicId, webhookPublicId),
|
||||
});
|
||||
|
||||
return result
|
||||
? {
|
||||
...result,
|
||||
events: parseEvents(result.events),
|
||||
}
|
||||
: null;
|
||||
};
|
||||
|
||||
export const getAllByWorkspaceId = async (
|
||||
db: dbClient,
|
||||
workspaceId: number,
|
||||
) => {
|
||||
const results = await db.query.workspaceWebhooks.findMany({
|
||||
columns: {
|
||||
publicId: true,
|
||||
name: true,
|
||||
url: true,
|
||||
events: true,
|
||||
active: true,
|
||||
createdAt: true,
|
||||
updatedAt: true,
|
||||
},
|
||||
where: eq(workspaceWebhooks.workspaceId, workspaceId),
|
||||
});
|
||||
|
||||
return results.map((webhook) => ({
|
||||
...webhook,
|
||||
events: parseEvents(webhook.events),
|
||||
}));
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns active webhooks with secrets for server-side delivery (HMAC signing).
|
||||
* DO NOT expose this via tRPC or any client-facing endpoint.
|
||||
* Use getAllByWorkspaceId (which omits secrets) for the admin list endpoint.
|
||||
*/
|
||||
export const getActiveByWorkspaceId = async (
|
||||
db: dbClient,
|
||||
workspaceId: number,
|
||||
) => {
|
||||
const results = await db.query.workspaceWebhooks.findMany({
|
||||
columns: {
|
||||
publicId: true,
|
||||
url: true,
|
||||
secret: true,
|
||||
events: true,
|
||||
},
|
||||
where: and(
|
||||
eq(workspaceWebhooks.workspaceId, workspaceId),
|
||||
eq(workspaceWebhooks.active, true),
|
||||
),
|
||||
});
|
||||
|
||||
return results.map((webhook) => ({
|
||||
...webhook,
|
||||
events: parseEvents(webhook.events),
|
||||
}));
|
||||
};
|
||||
|
||||
export const hardDelete = (db: dbClient, webhookPublicId: string) => {
|
||||
return db
|
||||
.delete(workspaceWebhooks)
|
||||
.where(eq(workspaceWebhooks.publicId, webhookPublicId));
|
||||
};
|
||||
@@ -14,4 +14,3 @@ export * from "./subscriptions";
|
||||
export * from "./workspaceInviteLinks";
|
||||
export * from "./permissions";
|
||||
export * from "./notifications";
|
||||
export * from "./webhooks";
|
||||
|
||||
@@ -1,59 +0,0 @@
|
||||
import { relations } from "drizzle-orm";
|
||||
import {
|
||||
bigint,
|
||||
bigserial,
|
||||
boolean,
|
||||
index,
|
||||
pgTable,
|
||||
text,
|
||||
timestamp,
|
||||
uuid,
|
||||
varchar,
|
||||
} from "drizzle-orm/pg-core";
|
||||
|
||||
import { users } from "./users";
|
||||
import { workspaces } from "./workspaces";
|
||||
|
||||
export const webhookEvents = [
|
||||
"card.created",
|
||||
"card.updated",
|
||||
"card.moved",
|
||||
"card.deleted",
|
||||
] as const;
|
||||
export type WebhookEvent = (typeof webhookEvents)[number];
|
||||
|
||||
export const workspaceWebhooks = pgTable("workspace_webhooks", {
|
||||
id: bigserial("id", { mode: "number" }).primaryKey(),
|
||||
publicId: varchar("publicId", { length: 12 }).notNull().unique(),
|
||||
workspaceId: bigint("workspaceId", { mode: "number" })
|
||||
.notNull()
|
||||
.references(() => workspaces.id, { onDelete: "cascade" }),
|
||||
name: varchar("name", { length: 255 }).notNull(),
|
||||
url: varchar("url", { length: 2048 }).notNull(),
|
||||
secret: text("secret"),
|
||||
events: text("events").notNull(), // JSON array of webhook events
|
||||
active: boolean("active").notNull().default(true),
|
||||
createdBy: uuid("createdBy")
|
||||
.notNull()
|
||||
.references(() => users.id, { onDelete: "cascade" }),
|
||||
createdAt: timestamp("createdAt").defaultNow().notNull(),
|
||||
updatedAt: timestamp("updatedAt"),
|
||||
}, (table) => [
|
||||
index("workspace_webhooks_workspace_idx").on(table.workspaceId),
|
||||
]).enableRLS();
|
||||
|
||||
export const workspaceWebhooksRelations = relations(
|
||||
workspaceWebhooks,
|
||||
({ one }) => ({
|
||||
workspace: one(workspaces, {
|
||||
fields: [workspaceWebhooks.workspaceId],
|
||||
references: [workspaces.id],
|
||||
relationName: "workspaceWebhooksWorkspace",
|
||||
}),
|
||||
createdByUser: one(users, {
|
||||
fields: [workspaceWebhooks.createdBy],
|
||||
references: [users.id],
|
||||
relationName: "workspaceWebhooksCreatedByUser",
|
||||
}),
|
||||
}),
|
||||
);
|
||||
143
pnpm-lock.yaml
generated
143
pnpm-lock.yaml
generated
@@ -254,9 +254,6 @@ importers:
|
||||
'@lingui/cli':
|
||||
specifier: ^5.3.2
|
||||
version: 5.4.1(typescript@5.9.2)
|
||||
'@lingui/format-json':
|
||||
specifier: ^5.9.1
|
||||
version: 5.9.1(typescript@5.9.2)
|
||||
'@lingui/loader':
|
||||
specifier: ^5.3.2
|
||||
version: 5.4.1(typescript@5.9.2)(webpack@5.101.3)
|
||||
@@ -344,7 +341,7 @@ importers:
|
||||
version: link:../../tooling/typescript
|
||||
eslint:
|
||||
specifier: 'catalog:'
|
||||
version: 9.34.0(jiti@2.6.1)
|
||||
version: 9.34.0(jiti@2.4.2)
|
||||
prettier:
|
||||
specifier: 'catalog:'
|
||||
version: 3.6.2
|
||||
@@ -384,7 +381,7 @@ importers:
|
||||
version: link:../../tooling/typescript
|
||||
eslint:
|
||||
specifier: 'catalog:'
|
||||
version: 9.34.0(jiti@2.6.1)
|
||||
version: 9.34.0(jiti@2.4.2)
|
||||
prettier:
|
||||
specifier: 'catalog:'
|
||||
version: 3.6.2
|
||||
@@ -439,7 +436,7 @@ importers:
|
||||
version: 0.28.1
|
||||
eslint:
|
||||
specifier: 'catalog:'
|
||||
version: 9.34.0(jiti@2.6.1)
|
||||
version: 9.34.0(jiti@2.4.2)
|
||||
prettier:
|
||||
specifier: 'catalog:'
|
||||
version: 3.6.2
|
||||
@@ -476,7 +473,7 @@ importers:
|
||||
version: 6.4.19
|
||||
eslint:
|
||||
specifier: 'catalog:'
|
||||
version: 9.34.0(jiti@2.6.1)
|
||||
version: 9.34.0(jiti@2.4.2)
|
||||
prettier:
|
||||
specifier: 'catalog:'
|
||||
version: 3.6.2
|
||||
@@ -516,7 +513,7 @@ importers:
|
||||
version: link:../../tooling/typescript
|
||||
eslint:
|
||||
specifier: 'catalog:'
|
||||
version: 9.34.0(jiti@2.6.1)
|
||||
version: 9.34.0(jiti@2.4.2)
|
||||
prettier:
|
||||
specifier: 'catalog:'
|
||||
version: 3.6.2
|
||||
@@ -541,7 +538,7 @@ importers:
|
||||
version: link:../../tooling/typescript
|
||||
eslint:
|
||||
specifier: 'catalog:'
|
||||
version: 9.34.0(jiti@2.6.1)
|
||||
version: 9.34.0(jiti@2.4.2)
|
||||
prettier:
|
||||
specifier: 'catalog:'
|
||||
version: 3.6.2
|
||||
@@ -553,28 +550,28 @@ importers:
|
||||
dependencies:
|
||||
'@eslint/compat':
|
||||
specifier: ^1.2.3
|
||||
version: 1.3.2(eslint@9.34.0(jiti@2.6.1))
|
||||
version: 1.3.2(eslint@9.34.0(jiti@2.4.2))
|
||||
'@next/eslint-plugin-next':
|
||||
specifier: ^14.2.15
|
||||
version: 14.2.32
|
||||
eslint-plugin-import:
|
||||
specifier: ^2.31.0
|
||||
version: 2.32.0(@typescript-eslint/parser@8.41.0(eslint@9.34.0(jiti@2.6.1))(typescript@5.9.2))(eslint@9.34.0(jiti@2.6.1))
|
||||
version: 2.32.0(@typescript-eslint/parser@8.41.0(eslint@9.34.0(jiti@2.4.2))(typescript@5.9.2))(eslint@9.34.0(jiti@2.4.2))
|
||||
eslint-plugin-jsx-a11y:
|
||||
specifier: ^6.10.2
|
||||
version: 6.10.2(eslint@9.34.0(jiti@2.6.1))
|
||||
version: 6.10.2(eslint@9.34.0(jiti@2.4.2))
|
||||
eslint-plugin-react:
|
||||
specifier: ^7.37.2
|
||||
version: 7.37.5(eslint@9.34.0(jiti@2.6.1))
|
||||
version: 7.37.5(eslint@9.34.0(jiti@2.4.2))
|
||||
eslint-plugin-react-hooks:
|
||||
specifier: ^5.0.0
|
||||
version: 5.2.0(eslint@9.34.0(jiti@2.6.1))
|
||||
version: 5.2.0(eslint@9.34.0(jiti@2.4.2))
|
||||
eslint-plugin-turbo:
|
||||
specifier: ^2.3.1
|
||||
version: 2.5.6(eslint@9.34.0(jiti@2.6.1))(turbo@2.5.6)
|
||||
version: 2.5.6(eslint@9.34.0(jiti@2.4.2))(turbo@2.5.6)
|
||||
typescript-eslint:
|
||||
specifier: ^8.15.0
|
||||
version: 8.41.0(eslint@9.34.0(jiti@2.6.1))(typescript@5.9.2)
|
||||
version: 8.41.0(eslint@9.34.0(jiti@2.4.2))(typescript@5.9.2)
|
||||
devDependencies:
|
||||
'@kan/prettier-config':
|
||||
specifier: workspace:*
|
||||
@@ -587,7 +584,7 @@ importers:
|
||||
version: 8.42.3
|
||||
eslint:
|
||||
specifier: 'catalog:'
|
||||
version: 9.34.0(jiti@2.6.1)
|
||||
version: 9.34.0(jiti@2.4.2)
|
||||
prettier:
|
||||
specifier: 'catalog:'
|
||||
version: 3.6.2
|
||||
@@ -640,7 +637,7 @@ importers:
|
||||
version: link:../typescript
|
||||
eslint:
|
||||
specifier: 'catalog:'
|
||||
version: 9.34.0(jiti@2.6.1)
|
||||
version: 9.34.0(jiti@2.4.2)
|
||||
prettier:
|
||||
specifier: 'catalog:'
|
||||
version: 3.6.2
|
||||
@@ -2676,10 +2673,6 @@ packages:
|
||||
resolution: {integrity: sha512-aDkj/bMSr/mCL8Nr1TS52v0GLCuVa4YqtRz+WvUCFZw/ovVInX0hKq1TClx/bSlhu60FzB/CbclxFMBw8aLVUg==}
|
||||
engines: {node: '>=20.0.0'}
|
||||
|
||||
'@lingui/conf@5.9.1':
|
||||
resolution: {integrity: sha512-Dr1CV4P3INJZZzvLcksgML4kusHQS3pJyONcZt40l/T74LOETUSk2cxFW5epizWU46FpN2ThusvC2yb0L8o1jA==}
|
||||
engines: {node: '>=20.0.0'}
|
||||
|
||||
'@lingui/core@5.4.1':
|
||||
resolution: {integrity: sha512-4FeIh56PH5vziPg2BYo4XYWWOHE4XaY/XR8Jakwn0/qwtLpydWMNVpZOpGWi7nfPZtcLaJLmZKup6UNxEl1Pfw==}
|
||||
engines: {node: '>=20.0.0'}
|
||||
@@ -2692,10 +2685,6 @@ packages:
|
||||
babel-plugin-macros:
|
||||
optional: true
|
||||
|
||||
'@lingui/format-json@5.9.1':
|
||||
resolution: {integrity: sha512-Kp/saH3jKJPlAzjcDJd+yqePoC6PZOc8cbO1mL/mN8mOz5bF9w+LnX4KntgZz1MjJwP12fqtGbr6AxDevwnmWw==}
|
||||
engines: {node: '>=20.0.0'}
|
||||
|
||||
'@lingui/format-po@5.4.1':
|
||||
resolution: {integrity: sha512-IBVq3RRLNEVRzNZcdEw0qpM5NKX4e9wDmvJMorkR2OYrgTbhWx5gDYhXpEZ9yqtuEVhILMdriVNjAAUnDAJibA==}
|
||||
engines: {node: '>=20.0.0'}
|
||||
@@ -6168,10 +6157,6 @@ packages:
|
||||
resolution: {integrity: sha512-rg9zJN+G4n2nfJl5MW3BMygZX56zKPNVEYYqq7adpmMh4Jn2QNEwhvQlFy6jPVdcod7txZtKHWnyZiA3a0zP7A==}
|
||||
hasBin: true
|
||||
|
||||
jiti@2.6.1:
|
||||
resolution: {integrity: sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==}
|
||||
hasBin: true
|
||||
|
||||
jose@6.1.2:
|
||||
resolution: {integrity: sha512-MpcPtHLE5EmztuFIqB0vzHAWJPpmN1E6L4oo+kze56LIs3MyXIj9ZHMDxqOvkP38gBR7K1v3jqd4WU2+nrfONQ==}
|
||||
|
||||
@@ -10466,16 +10451,16 @@ snapshots:
|
||||
eslint: 9.34.0(jiti@1.21.7)
|
||||
eslint-visitor-keys: 3.4.3
|
||||
|
||||
'@eslint-community/eslint-utils@4.7.0(eslint@9.34.0(jiti@2.6.1))':
|
||||
'@eslint-community/eslint-utils@4.7.0(eslint@9.34.0(jiti@2.4.2))':
|
||||
dependencies:
|
||||
eslint: 9.34.0(jiti@2.6.1)
|
||||
eslint: 9.34.0(jiti@2.4.2)
|
||||
eslint-visitor-keys: 3.4.3
|
||||
|
||||
'@eslint-community/regexpp@4.12.1': {}
|
||||
|
||||
'@eslint/compat@1.3.2(eslint@9.34.0(jiti@2.6.1))':
|
||||
'@eslint/compat@1.3.2(eslint@9.34.0(jiti@2.4.2))':
|
||||
optionalDependencies:
|
||||
eslint: 9.34.0(jiti@2.6.1)
|
||||
eslint: 9.34.0(jiti@2.4.2)
|
||||
|
||||
'@eslint/config-array@0.21.0':
|
||||
dependencies:
|
||||
@@ -10886,16 +10871,6 @@ snapshots:
|
||||
transitivePeerDependencies:
|
||||
- typescript
|
||||
|
||||
'@lingui/conf@5.9.1(typescript@5.9.2)':
|
||||
dependencies:
|
||||
'@babel/runtime': 7.28.3
|
||||
cosmiconfig: 8.3.6(typescript@5.9.2)
|
||||
jest-validate: 29.7.0
|
||||
jiti: 2.6.1
|
||||
picocolors: 1.1.1
|
||||
transitivePeerDependencies:
|
||||
- typescript
|
||||
|
||||
'@lingui/core@5.4.1(@lingui/babel-plugin-lingui-macro@5.4.1(typescript@5.9.2))':
|
||||
dependencies:
|
||||
'@babel/runtime': 7.28.3
|
||||
@@ -10903,12 +10878,6 @@ snapshots:
|
||||
optionalDependencies:
|
||||
'@lingui/babel-plugin-lingui-macro': 5.4.1(typescript@5.9.2)
|
||||
|
||||
'@lingui/format-json@5.9.1(typescript@5.9.2)':
|
||||
dependencies:
|
||||
'@lingui/conf': 5.9.1(typescript@5.9.2)
|
||||
transitivePeerDependencies:
|
||||
- typescript
|
||||
|
||||
'@lingui/format-po@5.4.1(typescript@5.9.2)':
|
||||
dependencies:
|
||||
'@lingui/conf': 5.4.1(typescript@5.9.2)
|
||||
@@ -12570,15 +12539,15 @@ snapshots:
|
||||
dependencies:
|
||||
'@types/yargs-parser': 21.0.3
|
||||
|
||||
'@typescript-eslint/eslint-plugin@8.41.0(@typescript-eslint/parser@8.41.0(eslint@9.34.0(jiti@2.6.1))(typescript@5.9.2))(eslint@9.34.0(jiti@2.6.1))(typescript@5.9.2)':
|
||||
'@typescript-eslint/eslint-plugin@8.41.0(@typescript-eslint/parser@8.41.0(eslint@9.34.0(jiti@2.4.2))(typescript@5.9.2))(eslint@9.34.0(jiti@2.4.2))(typescript@5.9.2)':
|
||||
dependencies:
|
||||
'@eslint-community/regexpp': 4.12.1
|
||||
'@typescript-eslint/parser': 8.41.0(eslint@9.34.0(jiti@2.6.1))(typescript@5.9.2)
|
||||
'@typescript-eslint/parser': 8.41.0(eslint@9.34.0(jiti@2.4.2))(typescript@5.9.2)
|
||||
'@typescript-eslint/scope-manager': 8.41.0
|
||||
'@typescript-eslint/type-utils': 8.41.0(eslint@9.34.0(jiti@2.6.1))(typescript@5.9.2)
|
||||
'@typescript-eslint/utils': 8.41.0(eslint@9.34.0(jiti@2.6.1))(typescript@5.9.2)
|
||||
'@typescript-eslint/type-utils': 8.41.0(eslint@9.34.0(jiti@2.4.2))(typescript@5.9.2)
|
||||
'@typescript-eslint/utils': 8.41.0(eslint@9.34.0(jiti@2.4.2))(typescript@5.9.2)
|
||||
'@typescript-eslint/visitor-keys': 8.41.0
|
||||
eslint: 9.34.0(jiti@2.6.1)
|
||||
eslint: 9.34.0(jiti@2.4.2)
|
||||
graphemer: 1.4.0
|
||||
ignore: 7.0.5
|
||||
natural-compare: 1.4.0
|
||||
@@ -12587,14 +12556,14 @@ snapshots:
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
'@typescript-eslint/parser@8.41.0(eslint@9.34.0(jiti@2.6.1))(typescript@5.9.2)':
|
||||
'@typescript-eslint/parser@8.41.0(eslint@9.34.0(jiti@2.4.2))(typescript@5.9.2)':
|
||||
dependencies:
|
||||
'@typescript-eslint/scope-manager': 8.41.0
|
||||
'@typescript-eslint/types': 8.41.0
|
||||
'@typescript-eslint/typescript-estree': 8.41.0(typescript@5.9.2)
|
||||
'@typescript-eslint/visitor-keys': 8.41.0
|
||||
debug: 4.4.3
|
||||
eslint: 9.34.0(jiti@2.6.1)
|
||||
eslint: 9.34.0(jiti@2.4.2)
|
||||
typescript: 5.9.2
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
@@ -12617,13 +12586,13 @@ snapshots:
|
||||
dependencies:
|
||||
typescript: 5.9.2
|
||||
|
||||
'@typescript-eslint/type-utils@8.41.0(eslint@9.34.0(jiti@2.6.1))(typescript@5.9.2)':
|
||||
'@typescript-eslint/type-utils@8.41.0(eslint@9.34.0(jiti@2.4.2))(typescript@5.9.2)':
|
||||
dependencies:
|
||||
'@typescript-eslint/types': 8.41.0
|
||||
'@typescript-eslint/typescript-estree': 8.41.0(typescript@5.9.2)
|
||||
'@typescript-eslint/utils': 8.41.0(eslint@9.34.0(jiti@2.6.1))(typescript@5.9.2)
|
||||
'@typescript-eslint/utils': 8.41.0(eslint@9.34.0(jiti@2.4.2))(typescript@5.9.2)
|
||||
debug: 4.4.3
|
||||
eslint: 9.34.0(jiti@2.6.1)
|
||||
eslint: 9.34.0(jiti@2.4.2)
|
||||
ts-api-utils: 2.1.0(typescript@5.9.2)
|
||||
typescript: 5.9.2
|
||||
transitivePeerDependencies:
|
||||
@@ -12647,13 +12616,13 @@ snapshots:
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
'@typescript-eslint/utils@8.41.0(eslint@9.34.0(jiti@2.6.1))(typescript@5.9.2)':
|
||||
'@typescript-eslint/utils@8.41.0(eslint@9.34.0(jiti@2.4.2))(typescript@5.9.2)':
|
||||
dependencies:
|
||||
'@eslint-community/eslint-utils': 4.7.0(eslint@9.34.0(jiti@2.6.1))
|
||||
'@eslint-community/eslint-utils': 4.7.0(eslint@9.34.0(jiti@2.4.2))
|
||||
'@typescript-eslint/scope-manager': 8.41.0
|
||||
'@typescript-eslint/types': 8.41.0
|
||||
'@typescript-eslint/typescript-estree': 8.41.0(typescript@5.9.2)
|
||||
eslint: 9.34.0(jiti@2.6.1)
|
||||
eslint: 9.34.0(jiti@2.4.2)
|
||||
typescript: 5.9.2
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
@@ -13979,17 +13948,17 @@ snapshots:
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
eslint-module-utils@2.12.1(@typescript-eslint/parser@8.41.0(eslint@9.34.0(jiti@2.6.1))(typescript@5.9.2))(eslint-import-resolver-node@0.3.9)(eslint@9.34.0(jiti@2.6.1)):
|
||||
eslint-module-utils@2.12.1(@typescript-eslint/parser@8.41.0(eslint@9.34.0(jiti@2.4.2))(typescript@5.9.2))(eslint-import-resolver-node@0.3.9)(eslint@9.34.0(jiti@2.4.2)):
|
||||
dependencies:
|
||||
debug: 3.2.7
|
||||
optionalDependencies:
|
||||
'@typescript-eslint/parser': 8.41.0(eslint@9.34.0(jiti@2.6.1))(typescript@5.9.2)
|
||||
eslint: 9.34.0(jiti@2.6.1)
|
||||
'@typescript-eslint/parser': 8.41.0(eslint@9.34.0(jiti@2.4.2))(typescript@5.9.2)
|
||||
eslint: 9.34.0(jiti@2.4.2)
|
||||
eslint-import-resolver-node: 0.3.9
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.41.0(eslint@9.34.0(jiti@2.6.1))(typescript@5.9.2))(eslint@9.34.0(jiti@2.6.1)):
|
||||
eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.41.0(eslint@9.34.0(jiti@2.4.2))(typescript@5.9.2))(eslint@9.34.0(jiti@2.4.2)):
|
||||
dependencies:
|
||||
'@rtsao/scc': 1.1.0
|
||||
array-includes: 3.1.9
|
||||
@@ -13998,9 +13967,9 @@ snapshots:
|
||||
array.prototype.flatmap: 1.3.3
|
||||
debug: 3.2.7
|
||||
doctrine: 2.1.0
|
||||
eslint: 9.34.0(jiti@2.6.1)
|
||||
eslint: 9.34.0(jiti@2.4.2)
|
||||
eslint-import-resolver-node: 0.3.9
|
||||
eslint-module-utils: 2.12.1(@typescript-eslint/parser@8.41.0(eslint@9.34.0(jiti@2.6.1))(typescript@5.9.2))(eslint-import-resolver-node@0.3.9)(eslint@9.34.0(jiti@2.6.1))
|
||||
eslint-module-utils: 2.12.1(@typescript-eslint/parser@8.41.0(eslint@9.34.0(jiti@2.4.2))(typescript@5.9.2))(eslint-import-resolver-node@0.3.9)(eslint@9.34.0(jiti@2.4.2))
|
||||
hasown: 2.0.2
|
||||
is-core-module: 2.16.1
|
||||
is-glob: 4.0.3
|
||||
@@ -14012,13 +13981,13 @@ snapshots:
|
||||
string.prototype.trimend: 1.0.9
|
||||
tsconfig-paths: 3.15.0
|
||||
optionalDependencies:
|
||||
'@typescript-eslint/parser': 8.41.0(eslint@9.34.0(jiti@2.6.1))(typescript@5.9.2)
|
||||
'@typescript-eslint/parser': 8.41.0(eslint@9.34.0(jiti@2.4.2))(typescript@5.9.2)
|
||||
transitivePeerDependencies:
|
||||
- eslint-import-resolver-typescript
|
||||
- eslint-import-resolver-webpack
|
||||
- supports-color
|
||||
|
||||
eslint-plugin-jsx-a11y@6.10.2(eslint@9.34.0(jiti@2.6.1)):
|
||||
eslint-plugin-jsx-a11y@6.10.2(eslint@9.34.0(jiti@2.4.2)):
|
||||
dependencies:
|
||||
aria-query: 5.3.2
|
||||
array-includes: 3.1.9
|
||||
@@ -14028,7 +13997,7 @@ snapshots:
|
||||
axobject-query: 4.1.0
|
||||
damerau-levenshtein: 1.0.8
|
||||
emoji-regex: 9.2.2
|
||||
eslint: 9.34.0(jiti@2.6.1)
|
||||
eslint: 9.34.0(jiti@2.4.2)
|
||||
hasown: 2.0.2
|
||||
jsx-ast-utils: 3.3.5
|
||||
language-tags: 1.0.9
|
||||
@@ -14037,11 +14006,11 @@ snapshots:
|
||||
safe-regex-test: 1.1.0
|
||||
string.prototype.includes: 2.0.1
|
||||
|
||||
eslint-plugin-react-hooks@5.2.0(eslint@9.34.0(jiti@2.6.1)):
|
||||
eslint-plugin-react-hooks@5.2.0(eslint@9.34.0(jiti@2.4.2)):
|
||||
dependencies:
|
||||
eslint: 9.34.0(jiti@2.6.1)
|
||||
eslint: 9.34.0(jiti@2.4.2)
|
||||
|
||||
eslint-plugin-react@7.37.5(eslint@9.34.0(jiti@2.6.1)):
|
||||
eslint-plugin-react@7.37.5(eslint@9.34.0(jiti@2.4.2)):
|
||||
dependencies:
|
||||
array-includes: 3.1.9
|
||||
array.prototype.findlast: 1.2.5
|
||||
@@ -14049,7 +14018,7 @@ snapshots:
|
||||
array.prototype.tosorted: 1.1.4
|
||||
doctrine: 2.1.0
|
||||
es-iterator-helpers: 1.2.1
|
||||
eslint: 9.34.0(jiti@2.6.1)
|
||||
eslint: 9.34.0(jiti@2.4.2)
|
||||
estraverse: 5.3.0
|
||||
hasown: 2.0.2
|
||||
jsx-ast-utils: 3.3.5
|
||||
@@ -14063,10 +14032,10 @@ snapshots:
|
||||
string.prototype.matchall: 4.0.12
|
||||
string.prototype.repeat: 1.0.0
|
||||
|
||||
eslint-plugin-turbo@2.5.6(eslint@9.34.0(jiti@2.6.1))(turbo@2.5.6):
|
||||
eslint-plugin-turbo@2.5.6(eslint@9.34.0(jiti@2.4.2))(turbo@2.5.6):
|
||||
dependencies:
|
||||
dotenv: 16.0.3
|
||||
eslint: 9.34.0(jiti@2.6.1)
|
||||
eslint: 9.34.0(jiti@2.4.2)
|
||||
turbo: 2.5.6
|
||||
|
||||
eslint-scope@5.1.1:
|
||||
@@ -14125,9 +14094,9 @@ snapshots:
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
eslint@9.34.0(jiti@2.6.1):
|
||||
eslint@9.34.0(jiti@2.4.2):
|
||||
dependencies:
|
||||
'@eslint-community/eslint-utils': 4.7.0(eslint@9.34.0(jiti@2.6.1))
|
||||
'@eslint-community/eslint-utils': 4.7.0(eslint@9.34.0(jiti@2.4.2))
|
||||
'@eslint-community/regexpp': 4.12.1
|
||||
'@eslint/config-array': 0.21.0
|
||||
'@eslint/config-helpers': 0.3.1
|
||||
@@ -14163,7 +14132,7 @@ snapshots:
|
||||
natural-compare: 1.4.0
|
||||
optionator: 0.9.4
|
||||
optionalDependencies:
|
||||
jiti: 2.6.1
|
||||
jiti: 2.4.2
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
@@ -14954,8 +14923,6 @@ snapshots:
|
||||
|
||||
jiti@2.4.2: {}
|
||||
|
||||
jiti@2.6.1: {}
|
||||
|
||||
jose@6.1.2: {}
|
||||
|
||||
jose@6.1.3: {}
|
||||
@@ -17718,13 +17685,13 @@ snapshots:
|
||||
possible-typed-array-names: 1.1.0
|
||||
reflect.getprototypeof: 1.0.10
|
||||
|
||||
typescript-eslint@8.41.0(eslint@9.34.0(jiti@2.6.1))(typescript@5.9.2):
|
||||
typescript-eslint@8.41.0(eslint@9.34.0(jiti@2.4.2))(typescript@5.9.2):
|
||||
dependencies:
|
||||
'@typescript-eslint/eslint-plugin': 8.41.0(@typescript-eslint/parser@8.41.0(eslint@9.34.0(jiti@2.6.1))(typescript@5.9.2))(eslint@9.34.0(jiti@2.6.1))(typescript@5.9.2)
|
||||
'@typescript-eslint/parser': 8.41.0(eslint@9.34.0(jiti@2.6.1))(typescript@5.9.2)
|
||||
'@typescript-eslint/eslint-plugin': 8.41.0(@typescript-eslint/parser@8.41.0(eslint@9.34.0(jiti@2.4.2))(typescript@5.9.2))(eslint@9.34.0(jiti@2.4.2))(typescript@5.9.2)
|
||||
'@typescript-eslint/parser': 8.41.0(eslint@9.34.0(jiti@2.4.2))(typescript@5.9.2)
|
||||
'@typescript-eslint/typescript-estree': 8.41.0(typescript@5.9.2)
|
||||
'@typescript-eslint/utils': 8.41.0(eslint@9.34.0(jiti@2.6.1))(typescript@5.9.2)
|
||||
eslint: 9.34.0(jiti@2.6.1)
|
||||
'@typescript-eslint/utils': 8.41.0(eslint@9.34.0(jiti@2.4.2))(typescript@5.9.2)
|
||||
eslint: 9.34.0(jiti@2.4.2)
|
||||
typescript: 5.9.2
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
Reference in New Issue
Block a user