Compare commits
21 Commits
fix/readd-
...
feat/smtp-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
09a190d6fb | ||
|
|
5586c26c12 | ||
|
|
2d7097eb83 | ||
|
|
2aa898da00 | ||
|
|
a01a826bac | ||
|
|
a29ea76024 | ||
|
|
e1c6bfd6cd | ||
|
|
c272c38f6b | ||
|
|
02d3d300fb | ||
|
|
0bd28f9f95 | ||
|
|
d0c2314ebf | ||
|
|
268871c348 | ||
|
|
2e11199764 | ||
|
|
fc6bb22400 | ||
|
|
1c75a948f9 | ||
|
|
3a48c797b7 | ||
|
|
b838e8040a | ||
|
|
f0e3c4c3e2 | ||
|
|
d6b0b5cd80 | ||
|
|
1707557bea | ||
|
|
f6114a56af |
33
.env.example
33
.env.example
@@ -1,28 +1,39 @@
|
|||||||
POSTGRES_URL=
|
# https://github.com/kanbn/kan?tab=readme-ov-file#environment-variables-)
|
||||||
|
|
||||||
EMAIL_FROM=
|
# Required environment variables
|
||||||
|
NEXT_PUBLIC_BASE_URL= # e.g. https://kan.bn
|
||||||
|
BETTER_AUTH_SECRET= # Random 32+ char string (can gen with: openssl rand -base64 24 | tr -dc 'a-zA-Z0-9' | head -c 32)
|
||||||
|
POSTGRES_URL= # e.g. postgresql://kan:your_password@your_host:5432/kan
|
||||||
|
|
||||||
|
# Only required if deploying from compose
|
||||||
|
POSTGRES_PASSWORD=
|
||||||
|
|
||||||
|
# SMTP (optional)
|
||||||
SMTP_HOST=
|
SMTP_HOST=
|
||||||
SMTP_PORT=
|
SMTP_PORT=465 # Port 587 will not work
|
||||||
SMTP_USER=
|
SMTP_USER=
|
||||||
SMTP_PASSWORD=
|
SMTP_PASSWORD=
|
||||||
|
EMAIL_FROM= # e.g. "Kan <hello@mail.kan.bn>"
|
||||||
|
|
||||||
NEXT_PUBLIC_BASE_URL=
|
# S3 storage (optional)
|
||||||
NEXT_PUBLIC_STORAGE_URL=
|
|
||||||
NEXT_PUBLIC_AVATAR_BUCKET_NAME=
|
|
||||||
NEXT_PUBLIC_ALLOW_CREDENTIALS=
|
|
||||||
NEXT_PUBLIC_DISABLE_SIGN_UP=
|
|
||||||
|
|
||||||
S3_REGION=
|
S3_REGION=
|
||||||
S3_ENDPOINT=
|
S3_ENDPOINT=
|
||||||
S3_ACCESS_KEY_ID=
|
S3_ACCESS_KEY_ID=
|
||||||
S3_SECRET_ACCESS_KEY=
|
S3_SECRET_ACCESS_KEY=
|
||||||
|
NEXT_PUBLIC_STORAGE_URL=
|
||||||
|
NEXT_PUBLIC_AVATAR_BUCKET_NAME=
|
||||||
|
NEXT_PUBLIC_STORAGE_DOMAIN=
|
||||||
|
|
||||||
BETTER_AUTH_SECRET=
|
# Auth config (optional)
|
||||||
BETTER_AUTH_TRUSTED_ORIGINS=
|
NEXT_PUBLIC_ALLOW_CREDENTIALS=
|
||||||
|
NEXT_PUBLIC_DISABLE_SIGN_UP=
|
||||||
|
|
||||||
|
# Integration providers (optional)
|
||||||
TRELLO_APP_API_KEY=
|
TRELLO_APP_API_KEY=
|
||||||
TRELLO_APP_SECRET=
|
TRELLO_APP_SECRET=
|
||||||
|
|
||||||
|
# OAuth providers (optional)
|
||||||
|
BETTER_AUTH_TRUSTED_ORIGINS=
|
||||||
GOOGLE_CLIENT_ID=
|
GOOGLE_CLIENT_ID=
|
||||||
GOOGLE_CLIENT_SECRET=
|
GOOGLE_CLIENT_SECRET=
|
||||||
DISCORD_CLIENT_ID=
|
DISCORD_CLIENT_ID=
|
||||||
|
|||||||
71
README.md
71
README.md
@@ -47,34 +47,67 @@ See our [roadmap](https://kan.bn/kan/roadmap) for upcoming features.
|
|||||||
|
|
||||||
## Self Hosting 🐳
|
## Self Hosting 🐳
|
||||||
|
|
||||||
### PostgreSQL Database Setup
|
The easiest way to self-host Kan is with Docker Compose. This will set up everything for you including your postgres database.
|
||||||
|
|
||||||
Kan requires a PostgreSQL database. You can run one using the official PostgreSQL Docker image:
|
1. Create a new file called `docker-compose.yml` and paste the following configuration:
|
||||||
|
|
||||||
```bash
|
```yaml
|
||||||
# Run PostgreSQL in a container using the official postgres image
|
services:
|
||||||
docker run -d \
|
web:
|
||||||
--name kan-db \
|
image: ghcr.io/kanbn/kan:latest
|
||||||
-e POSTGRES_DB=kan \
|
container_name: kan-web
|
||||||
-e POSTGRES_USER=kan_user \
|
ports:
|
||||||
-e POSTGRES_PASSWORD=your_secure_password \
|
- "3000:3000"
|
||||||
-p 5432:5432 \
|
networks:
|
||||||
-v kan_postgres_data:/var/lib/postgresql/data \
|
- kan-network
|
||||||
postgres:15
|
environment:
|
||||||
|
NEXT_PUBLIC_BASE_URL: http://localhost:3000
|
||||||
|
BETTER_AUTH_SECRET: your_auth_secret
|
||||||
|
POSTGRES_URL: postgresql://kan:your_postgres_password@postgres:5432/kan_db
|
||||||
|
NEXT_PUBLIC_ALLOW_CREDENTIALS: true
|
||||||
|
depends_on:
|
||||||
|
- postgres
|
||||||
|
restart: unless-stopped
|
||||||
|
|
||||||
# Your POSTGRES_URL should be:
|
postgres:
|
||||||
# postgres://kan_user:your_secure_password@your_host:5432/kan
|
image: postgres:15
|
||||||
|
container_name: kan-db
|
||||||
|
environment:
|
||||||
|
POSTGRES_DB: kan_db
|
||||||
|
POSTGRES_USER: kan
|
||||||
|
POSTGRES_PASSWORD: your_postgres_password
|
||||||
|
ports:
|
||||||
|
- 5432:5432
|
||||||
|
volumes:
|
||||||
|
- kan_postgres_data:/var/lib/postgresql/data
|
||||||
|
restart: unless-stopped
|
||||||
|
networks:
|
||||||
|
- kan-network
|
||||||
|
|
||||||
|
networks:
|
||||||
|
kan-network:
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
kan_postgres_data:
|
||||||
```
|
```
|
||||||
|
|
||||||
### Kan Application Deployment
|
2. Start the containers in detached mode:
|
||||||
|
|
||||||
Deploy Kan with Docker using our pre-built image:
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
docker pull ghcr.io/kanbn/kan:latest && docker run -it -p 3000:3000 --env-file .env ghcr.io/kanbn/kan:latest
|
docker compose up -d
|
||||||
```
|
```
|
||||||
|
|
||||||
Make sure to create a `.env` file with the required environment variables (see the Environment Variables section below).
|
3. Access Kan at http://localhost:3000
|
||||||
|
|
||||||
|
The application will be running in the background. You can manage the containers using these commands:
|
||||||
|
|
||||||
|
- To stop the containers: `docker compose down`
|
||||||
|
- To view logs: `docker compose logs -f`
|
||||||
|
- To restart the containers: `docker compose restart`
|
||||||
|
|
||||||
|
For the complete Docker Compose configuration, see [docker-compose.yml](./docker-compose.yml) in the repository.
|
||||||
|
|
||||||
|
> **Note**: The Docker Compose configuration shown above is a minimal example. For a complete setup with all features (email, OAuth, file uploads, etc.), you'll need to create a `.env` file with the required environment variables. See the Environment Variables section below for the full list of available options.
|
||||||
|
|
||||||
## Local Development 🧑💻
|
## Local Development 🧑💻
|
||||||
|
|
||||||
|
|||||||
@@ -53,8 +53,14 @@ RUN --mount=type=cache,id=pnpm,target=~/.pnpm-store pnpm install --frozen-lockfi
|
|||||||
# Copy source code of isolated subworkspace
|
# Copy source code of isolated subworkspace
|
||||||
COPY --from=pruner /app/out/full/ .
|
COPY --from=pruner /app/out/full/ .
|
||||||
|
|
||||||
|
|
||||||
RUN pnpm build --filter=${PROJECT}
|
RUN pnpm build --filter=${PROJECT}
|
||||||
|
|
||||||
|
# # 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/
|
||||||
|
|
||||||
# 4. Final image - runner stage to run the application
|
# 4. Final image - runner stage to run the application
|
||||||
FROM base AS runner
|
FROM base AS runner
|
||||||
ARG APP_DIRNAME
|
ARG APP_DIRNAME
|
||||||
@@ -75,6 +81,5 @@ ARG PORT=3000
|
|||||||
ENV PORT=${PORT}
|
ENV PORT=${PORT}
|
||||||
EXPOSE ${PORT}
|
EXPOSE ${PORT}
|
||||||
|
|
||||||
# Run migration on start for now (until we have a better way to handle this)
|
|
||||||
CMD ["sh", "-c", "cd /app && pnpm db:migrate && cd /app/apps/web && pnpm start"]
|
CMD ["sh", "-c", "cd /app && pnpm db:migrate && cd /app/apps/web && pnpm start"]
|
||||||
|
|
||||||
|
|||||||
23
apps/web/entrypoint.sh
Normal file
23
apps/web/entrypoint.sh
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
# Run migrations from the Turborepo root context
|
||||||
|
echo "Running database migrations..."
|
||||||
|
# Navigate to the Turborepo root to run the db:migrate command
|
||||||
|
pnpm --prefix /app db:migrate
|
||||||
|
|
||||||
|
# Check if migration was successful
|
||||||
|
if [ $? -ne 0 ]; then
|
||||||
|
echo "\nDatabase migration failed! Exiting."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "\nStarting Next.js application..."
|
||||||
|
|
||||||
|
# Check if we should use standalone mode
|
||||||
|
if [ "$NEXT_PUBLIC_USE_STANDALONE_OUTPUT" = "true" ]; then
|
||||||
|
echo "Starting in standalone mode..."
|
||||||
|
exec node .next/standalone/apps/web/server.js
|
||||||
|
else
|
||||||
|
echo "Starting in standard mode..."
|
||||||
|
exec pnpm start
|
||||||
|
fi
|
||||||
@@ -10,6 +10,10 @@ configureRuntimeEnv();
|
|||||||
|
|
||||||
/** @type {import("next").NextConfig} */
|
/** @type {import("next").NextConfig} */
|
||||||
const config = {
|
const config = {
|
||||||
|
output:
|
||||||
|
env("NEXT_PUBLIC_USE_STANDALONE_OUTPUT") === "true"
|
||||||
|
? "standalone"
|
||||||
|
: undefined,
|
||||||
reactStrictMode: true,
|
reactStrictMode: true,
|
||||||
|
|
||||||
/** Enables hot reloading for local packages without a build step */
|
/** Enables hot reloading for local packages without a build step */
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
"private": true,
|
"private": true,
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "pnpm next build",
|
"build": "pnpm with-env next build",
|
||||||
"clean": "git clean -xdf .cache .next .turbo node_modules",
|
"clean": "git clean -xdf .cache .next .turbo node_modules",
|
||||||
"dev": "pnpm with-env next dev | pino-pretty",
|
"dev": "pnpm with-env next dev | pino-pretty",
|
||||||
"format": "prettier --check . --ignore-path ../../.gitignore",
|
"format": "prettier --check . --ignore-path ../../.gitignore",
|
||||||
@@ -42,6 +42,7 @@
|
|||||||
"next-runtime-env": "^1.7.2",
|
"next-runtime-env": "^1.7.2",
|
||||||
"nextjs-cors": "^2.2.0",
|
"nextjs-cors": "^2.2.0",
|
||||||
"pino": "^9.6.0",
|
"pino": "^9.6.0",
|
||||||
|
"posthog-js": "^1.254.0",
|
||||||
"react": "catalog:react18",
|
"react": "catalog:react18",
|
||||||
"react-beautiful-dnd": "^13.1.1",
|
"react-beautiful-dnd": "^13.1.1",
|
||||||
"react-contenteditable": "^3.3.7",
|
"react-contenteditable": "^3.3.7",
|
||||||
|
|||||||
@@ -17,8 +17,11 @@ export const env = createEnv({
|
|||||||
BETTER_AUTH_SECRET: z.string(),
|
BETTER_AUTH_SECRET: z.string(),
|
||||||
BETTER_AUTH_TRUSTED_ORIGINS: z
|
BETTER_AUTH_TRUSTED_ORIGINS: z
|
||||||
.string()
|
.string()
|
||||||
.refine((s) =>
|
.transform((s) => (s === "" ? undefined : s))
|
||||||
s.split(",").every((l) => z.string().url().safeParse(l).success),
|
.refine(
|
||||||
|
(s) =>
|
||||||
|
!s ||
|
||||||
|
s.split(",").every((l) => z.string().url().safeParse(l).success),
|
||||||
)
|
)
|
||||||
.optional(),
|
.optional(),
|
||||||
POSTGRES_URL: z.string().url(),
|
POSTGRES_URL: z.string().url(),
|
||||||
@@ -76,17 +79,26 @@ export const env = createEnv({
|
|||||||
client: {
|
client: {
|
||||||
NEXT_PUBLIC_KAN_ENV: z.string().optional(),
|
NEXT_PUBLIC_KAN_ENV: z.string().optional(),
|
||||||
NEXT_PUBLIC_UMAMI_ID: z.string().optional(),
|
NEXT_PUBLIC_UMAMI_ID: z.string().optional(),
|
||||||
|
NEXT_PUBLIC_POSTHOG_KEY: z.string().optional(),
|
||||||
|
NEXT_PUBLIC_POSTHOG_HOST: z.string().optional(),
|
||||||
|
NEXT_PUBLIC_USE_STANDALONE_OUTPUT: z.string().optional(),
|
||||||
NEXT_PUBLIC_BASE_URL: z.string().url(),
|
NEXT_PUBLIC_BASE_URL: z.string().url(),
|
||||||
NEXT_PUBLIC_STORAGE_URL: z.string().url().optional(),
|
NEXT_PUBLIC_STORAGE_URL: z.string().url().optional(),
|
||||||
NEXT_PUBLIC_AVATAR_BUCKET_NAME: z.string().optional(),
|
NEXT_PUBLIC_AVATAR_BUCKET_NAME: z.string().optional(),
|
||||||
NEXT_PUBLIC_STORAGE_DOMAIN: z.string().optional(),
|
NEXT_PUBLIC_STORAGE_DOMAIN: z.string().optional(),
|
||||||
NEXT_PUBLIC_ALLOW_CREDENTIALS: z
|
NEXT_PUBLIC_ALLOW_CREDENTIALS: z
|
||||||
.string()
|
.string()
|
||||||
.refine((s) => s.toLowerCase() === "true" || s.toLowerCase() === "false")
|
.transform((s) => (s === "" ? undefined : s))
|
||||||
|
.refine(
|
||||||
|
(s) => !s || s.toLowerCase() === "true" || s.toLowerCase() === "false",
|
||||||
|
)
|
||||||
.optional(),
|
.optional(),
|
||||||
NEXT_PUBLIC_DISABLE_SIGN_UP: z
|
NEXT_PUBLIC_DISABLE_SIGN_UP: z
|
||||||
.string()
|
.string()
|
||||||
.refine((s) => s.toLowerCase() === "true" || s.toLowerCase() === "false")
|
.transform((s) => (s === "" ? undefined : s))
|
||||||
|
.refine(
|
||||||
|
(s) => !s || s.toLowerCase() === "true" || s.toLowerCase() === "false",
|
||||||
|
)
|
||||||
.optional(),
|
.optional(),
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
@@ -96,12 +108,16 @@ export const env = createEnv({
|
|||||||
NEXT_PUBLIC_KAN_ENV: process.env.NEXT_PUBLIC_KAN_ENV,
|
NEXT_PUBLIC_KAN_ENV: process.env.NEXT_PUBLIC_KAN_ENV,
|
||||||
NODE_ENV: process.env.NODE_ENV,
|
NODE_ENV: process.env.NODE_ENV,
|
||||||
NEXT_PUBLIC_UMAMI_ID: process.env.NEXT_PUBLIC_UMAMI_ID,
|
NEXT_PUBLIC_UMAMI_ID: process.env.NEXT_PUBLIC_UMAMI_ID,
|
||||||
|
NEXT_PUBLIC_POSTHOG_KEY: process.env.NEXT_PUBLIC_POSTHOG_KEY,
|
||||||
|
NEXT_PUBLIC_POSTHOG_HOST: process.env.NEXT_PUBLIC_POSTHOG_HOST,
|
||||||
NEXT_PUBLIC_BASE_URL: process.env.NEXT_PUBLIC_BASE_URL,
|
NEXT_PUBLIC_BASE_URL: process.env.NEXT_PUBLIC_BASE_URL,
|
||||||
NEXT_PUBLIC_STORAGE_URL: process.env.NEXT_PUBLIC_STORAGE_URL,
|
NEXT_PUBLIC_STORAGE_URL: process.env.NEXT_PUBLIC_STORAGE_URL,
|
||||||
NEXT_PUBLIC_AVATAR_BUCKET_NAME: process.env.NEXT_PUBLIC_AVATAR_BUCKET_NAME,
|
NEXT_PUBLIC_AVATAR_BUCKET_NAME: process.env.NEXT_PUBLIC_AVATAR_BUCKET_NAME,
|
||||||
NEXT_PUBLIC_STORAGE_DOMAIN: process.env.NEXT_PUBLIC_STORAGE_DOMAIN,
|
NEXT_PUBLIC_STORAGE_DOMAIN: process.env.NEXT_PUBLIC_STORAGE_DOMAIN,
|
||||||
NEXT_PUBLIC_ALLOW_CREDENTIALS: process.env.NEXT_PUBLIC_ALLOW_CREDENTIALS,
|
NEXT_PUBLIC_ALLOW_CREDENTIALS: process.env.NEXT_PUBLIC_ALLOW_CREDENTIALS,
|
||||||
NEXT_PUBLIC_DISABLE_SIGN_UP: process.env.NEXT_PUBLIC_DISABLE_SIGN_UP,
|
NEXT_PUBLIC_DISABLE_SIGN_UP: process.env.NEXT_PUBLIC_DISABLE_SIGN_UP,
|
||||||
|
NEXT_PUBLIC_USE_STANDALONE_OUTPUT:
|
||||||
|
process.env.NEXT_PUBLIC_USE_STANDALONE_OUTPUT,
|
||||||
},
|
},
|
||||||
skipValidation:
|
skipValidation:
|
||||||
!!process.env.CI || process.env.npm_lifecycle_event === "lint",
|
!!process.env.CI || process.env.npm_lifecycle_event === "lint",
|
||||||
|
|||||||
@@ -2,7 +2,11 @@ import "~/styles/globals.css";
|
|||||||
|
|
||||||
import type { AppType } from "next/app";
|
import type { AppType } from "next/app";
|
||||||
import { Plus_Jakarta_Sans } from "next/font/google";
|
import { Plus_Jakarta_Sans } from "next/font/google";
|
||||||
|
import Script from "next/script";
|
||||||
import { env } from "next-runtime-env";
|
import { env } from "next-runtime-env";
|
||||||
|
import posthog from "posthog-js";
|
||||||
|
import { PostHogProvider } from "posthog-js/react";
|
||||||
|
import { useEffect } from "react";
|
||||||
|
|
||||||
import { ModalProvider } from "~/providers/modal";
|
import { ModalProvider } from "~/providers/modal";
|
||||||
import { PopupProvider } from "~/providers/popup";
|
import { PopupProvider } from "~/providers/popup";
|
||||||
@@ -21,6 +25,21 @@ export const metadata = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const MyApp: AppType = ({ Component, pageProps }) => {
|
const MyApp: AppType = ({ Component, pageProps }) => {
|
||||||
|
const posthogKey = env("NEXT_PUBLIC_POSTHOG_KEY");
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (posthogKey) {
|
||||||
|
posthog.init(posthogKey, {
|
||||||
|
api_host: env("NEXT_PUBLIC_POSTHOG_HOST"),
|
||||||
|
person_profiles: "identified_only",
|
||||||
|
defaults: "2025-05-24",
|
||||||
|
loaded: (posthog) => {
|
||||||
|
if (process.env.NODE_ENV === "development") posthog.debug();
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, [posthogKey]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<style jsx global>{`
|
<style jsx global>{`
|
||||||
@@ -32,7 +51,7 @@ const MyApp: AppType = ({ Component, pageProps }) => {
|
|||||||
}
|
}
|
||||||
`}</style>
|
`}</style>
|
||||||
{env("NEXT_PUBLIC_UMAMI_ID") && (
|
{env("NEXT_PUBLIC_UMAMI_ID") && (
|
||||||
<script
|
<Script
|
||||||
defer
|
defer
|
||||||
src="https://cloud.umami.is/script.js"
|
src="https://cloud.umami.is/script.js"
|
||||||
data-website-id={env("NEXT_PUBLIC_UMAMI_ID")}
|
data-website-id={env("NEXT_PUBLIC_UMAMI_ID")}
|
||||||
@@ -43,7 +62,13 @@ const MyApp: AppType = ({ Component, pageProps }) => {
|
|||||||
<ThemeProvider>
|
<ThemeProvider>
|
||||||
<ModalProvider>
|
<ModalProvider>
|
||||||
<PopupProvider>
|
<PopupProvider>
|
||||||
<Component {...pageProps} />
|
{posthogKey ? (
|
||||||
|
<PostHogProvider client={posthog}>
|
||||||
|
<Component {...pageProps} />
|
||||||
|
</PostHogProvider>
|
||||||
|
) : (
|
||||||
|
<Component {...pageProps} />
|
||||||
|
)}
|
||||||
</PopupProvider>
|
</PopupProvider>
|
||||||
</ModalProvider>
|
</ModalProvider>
|
||||||
</ThemeProvider>
|
</ThemeProvider>
|
||||||
|
|||||||
@@ -132,7 +132,11 @@ const Comment = ({
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
{!isEditing ? (
|
{!isEditing ? (
|
||||||
<p className="mt-2 text-sm">{comment}</p>
|
<ContentEditable
|
||||||
|
html={comment ?? ""}
|
||||||
|
disabled={true}
|
||||||
|
className="mt-2 text-sm"
|
||||||
|
/>
|
||||||
) : (
|
) : (
|
||||||
<form onSubmit={handleSubmit(onSubmit)}>
|
<form onSubmit={handleSubmit(onSubmit)}>
|
||||||
<ContentEditable
|
<ContentEditable
|
||||||
|
|||||||
63
cloud/docker-compose.yml
Normal file
63
cloud/docker-compose.yml
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
services:
|
||||||
|
web:
|
||||||
|
image: ghcr.io/kanbn/kan:latest
|
||||||
|
container_name: ${CONTAINER_NAME:-kan-web}
|
||||||
|
ports:
|
||||||
|
- "${WEB_PORT:-3000}:3000"
|
||||||
|
networks:
|
||||||
|
- dokploy-network
|
||||||
|
build:
|
||||||
|
context: ..
|
||||||
|
dockerfile: apps/web/Dockerfile
|
||||||
|
env_file:
|
||||||
|
- .env
|
||||||
|
restart: unless-stopped
|
||||||
|
environment:
|
||||||
|
- NEXT_PUBLIC_KAN_ENV=${NEXT_PUBLIC_KAN_ENV}
|
||||||
|
- NEXT_PUBLIC_BASE_URL=${NEXT_PUBLIC_BASE_URL}
|
||||||
|
- BETTER_AUTH_SECRET=${BETTER_AUTH_SECRET}
|
||||||
|
- POSTGRES_URL=${POSTGRES_URL}
|
||||||
|
- NEXT_PUBLIC_USE_STANDALONE_OUTPUT=${NEXT_PUBLIC_USE_STANDALONE_OUTPUT}
|
||||||
|
|
||||||
|
# Stripe
|
||||||
|
- STRIPE_SECRET_KEY=${STRIPE_SECRET_KEY}
|
||||||
|
- STRIPE_WEBHOOK_SECRET=${STRIPE_WEBHOOK_SECRET}
|
||||||
|
|
||||||
|
# SMTP
|
||||||
|
- SMTP_HOST=${SMTP_HOST}
|
||||||
|
- SMTP_PORT=${SMTP_PORT}
|
||||||
|
- SMTP_USER=${SMTP_USER}
|
||||||
|
- SMTP_PASSWORD=${SMTP_PASSWORD}
|
||||||
|
- EMAIL_FROM=${EMAIL_FROM}
|
||||||
|
|
||||||
|
# S3 storage
|
||||||
|
- S3_ACCESS_KEY_ID=${S3_ACCESS_KEY_ID}
|
||||||
|
- S3_SECRET_ACCESS_KEY=${S3_SECRET_ACCESS_KEY}
|
||||||
|
- S3_REGION=${S3_REGION}
|
||||||
|
- S3_ENDPOINT=${S3_ENDPOINT}
|
||||||
|
- NEXT_PUBLIC_STORAGE_URL=${NEXT_PUBLIC_STORAGE_URL}
|
||||||
|
- NEXT_PUBLIC_AVATAR_BUCKET_NAME=${NEXT_PUBLIC_AVATAR_BUCKET_NAME}
|
||||||
|
- NEXT_PUBLIC_STORAGE_DOMAIN=${NEXT_PUBLIC_STORAGE_DOMAIN}
|
||||||
|
|
||||||
|
# Auth config
|
||||||
|
- NEXT_PUBLIC_ALLOW_CREDENTIALS=${NEXT_PUBLIC_ALLOW_CREDENTIALS}
|
||||||
|
- NEXT_PUBLIC_DISABLE_SIGN_UP=${NEXT_PUBLIC_DISABLE_SIGN_UP}
|
||||||
|
|
||||||
|
# Integration providers
|
||||||
|
- TRELLO_APP_API_KEY=${TRELLO_APP_API_KEY}
|
||||||
|
- TRELLO_APP_SECRET=${TRELLO_APP_SECRET}
|
||||||
|
|
||||||
|
# OAuth providers (optional)
|
||||||
|
- BETTER_AUTH_TRUSTED_ORIGINS=${BETTER_AUTH_TRUSTED_ORIGINS}
|
||||||
|
- GOOGLE_CLIENT_ID=${GOOGLE_CLIENT_ID}
|
||||||
|
- GOOGLE_CLIENT_SECRET=${GOOGLE_CLIENT_SECRET}
|
||||||
|
|
||||||
|
# Analytics
|
||||||
|
- NEXT_PUBLIC_UMAMI_ID=${NEXT_PUBLIC_UMAMI_ID}
|
||||||
|
- NEXT_PUBLIC_POSTHOG_KEY=${NEXT_PUBLIC_POSTHOG_KEY}
|
||||||
|
- NEXT_PUBLIC_POSTHOG_HOST=${NEXT_PUBLIC_POSTHOG_HOST}
|
||||||
|
|
||||||
|
networks:
|
||||||
|
dokploy-network:
|
||||||
|
external: true
|
||||||
|
#bump
|
||||||
@@ -1,31 +1,49 @@
|
|||||||
version: "3.7"
|
|
||||||
|
|
||||||
services:
|
services:
|
||||||
web:
|
web:
|
||||||
image: ghcr.io/kanbn/kan:latest
|
image: ghcr.io/kanbn/kan:latest
|
||||||
|
container_name: ${CONTAINER_NAME:-kan-web}
|
||||||
ports:
|
ports:
|
||||||
- "3001:3000"
|
- "${WEB_PORT:-3000}:3000"
|
||||||
networks:
|
networks:
|
||||||
|
- kan-network
|
||||||
- dokploy-network
|
- dokploy-network
|
||||||
build:
|
build:
|
||||||
context: .
|
context: .
|
||||||
dockerfile: ./apps/web/Dockerfile
|
dockerfile: ./apps/web/Dockerfile.new
|
||||||
env_file:
|
env_file:
|
||||||
- .env
|
- .env
|
||||||
command: ["pnpm", "start"]
|
|
||||||
environment:
|
environment:
|
||||||
- EMAIL_FROM=${EMAIL_FROM}
|
# Required environment variables
|
||||||
|
- NEXT_PUBLIC_BASE_URL=${NEXT_PUBLIC_BASE_URL}
|
||||||
|
- BETTER_AUTH_SECRET=${BETTER_AUTH_SECRET}
|
||||||
|
- POSTGRES_URL=${POSTGRES_URL}
|
||||||
|
|
||||||
|
# SMTP (optional)
|
||||||
- SMTP_HOST=${SMTP_HOST}
|
- SMTP_HOST=${SMTP_HOST}
|
||||||
- SMTP_PORT=${SMTP_PORT}
|
- SMTP_PORT=${SMTP_PORT}
|
||||||
- SMTP_USER=${SMTP_USER}
|
- SMTP_USER=${SMTP_USER}
|
||||||
- SMTP_PASSWORD=${SMTP_PASSWORD}
|
- SMTP_PASSWORD=${SMTP_PASSWORD}
|
||||||
- BETTER_AUTH_SECRET=${BETTER_AUTH_SECRET}
|
- EMAIL_FROM=${EMAIL_FROM}
|
||||||
- BETTER_AUTH_TRUSTED_ORIGINS=${BETTER_AUTH_TRUSTED_ORIGINS}
|
|
||||||
- STRIPE_SECRET_KEY=${STRIPE_SECRET_KEY}
|
# S3 storage (optional)
|
||||||
- STRIPE_WEBHOOK_SECRET=${STRIPE_WEBHOOK_SECRET}
|
- S3_ACCESS_KEY_ID=${S3_ACCESS_KEY_ID}
|
||||||
- POSTGRES_URL=${POSTGRES_URL}
|
- S3_SECRET_ACCESS_KEY=${S3_SECRET_ACCESS_KEY}
|
||||||
|
- S3_REGION=${S3_REGION}
|
||||||
|
- S3_ENDPOINT=${S3_ENDPOINT}
|
||||||
|
- NEXT_PUBLIC_STORAGE_URL=${NEXT_PUBLIC_STORAGE_URL}
|
||||||
|
- NEXT_PUBLIC_AVATAR_BUCKET_NAME=${NEXT_PUBLIC_AVATAR_BUCKET_NAME}
|
||||||
|
- NEXT_PUBLIC_STORAGE_DOMAIN=${NEXT_PUBLIC_STORAGE_DOMAIN}
|
||||||
|
|
||||||
|
# Auth config (optional)
|
||||||
|
- NEXT_PUBLIC_ALLOW_CREDENTIALS=${NEXT_PUBLIC_ALLOW_CREDENTIALS}
|
||||||
|
- NEXT_PUBLIC_DISABLE_SIGN_UP=${NEXT_PUBLIC_DISABLE_SIGN_UP}
|
||||||
|
|
||||||
|
# Integration providers (optional)
|
||||||
- TRELLO_APP_API_KEY=${TRELLO_APP_API_KEY}
|
- TRELLO_APP_API_KEY=${TRELLO_APP_API_KEY}
|
||||||
- TRELLO_APP_SECRET=${TRELLO_APP_SECRET}
|
- TRELLO_APP_SECRET=${TRELLO_APP_SECRET}
|
||||||
|
|
||||||
|
# OAuth providers (optional)
|
||||||
|
- BETTER_AUTH_TRUSTED_ORIGINS=${BETTER_AUTH_TRUSTED_ORIGINS}
|
||||||
- GOOGLE_CLIENT_ID=${GOOGLE_CLIENT_ID}
|
- GOOGLE_CLIENT_ID=${GOOGLE_CLIENT_ID}
|
||||||
- GOOGLE_CLIENT_SECRET=${GOOGLE_CLIENT_SECRET}
|
- GOOGLE_CLIENT_SECRET=${GOOGLE_CLIENT_SECRET}
|
||||||
- DISCORD_CLIENT_ID=${DISCORD_CLIENT_ID}
|
- DISCORD_CLIENT_ID=${DISCORD_CLIENT_ID}
|
||||||
@@ -63,18 +81,37 @@ services:
|
|||||||
- APPLE_CLIENT_ID=${APPLE_CLIENT_ID}
|
- APPLE_CLIENT_ID=${APPLE_CLIENT_ID}
|
||||||
- APPLE_CLIENT_SECRET=${APPLE_CLIENT_SECRET}
|
- APPLE_CLIENT_SECRET=${APPLE_CLIENT_SECRET}
|
||||||
- APPLE_APP_BUNDLE_IDENTIFIER=${APPLE_APP_BUNDLE_IDENTIFIER}
|
- APPLE_APP_BUNDLE_IDENTIFIER=${APPLE_APP_BUNDLE_IDENTIFIER}
|
||||||
- S3_ACCESS_KEY_ID=${S3_ACCESS_KEY_ID}
|
|
||||||
- S3_SECRET_ACCESS_KEY=${S3_SECRET_ACCESS_KEY}
|
# Cloud (not required)
|
||||||
- S3_REGION=${S3_REGION}
|
|
||||||
- S3_ENDPOINT=${S3_ENDPOINT}
|
|
||||||
- NEXT_PUBLIC_KAN_ENV=${NEXT_PUBLIC_KAN_ENV}
|
- NEXT_PUBLIC_KAN_ENV=${NEXT_PUBLIC_KAN_ENV}
|
||||||
- NEXT_PUBLIC_BASE_URL=${NEXT_PUBLIC_BASE_URL}
|
|
||||||
- NEXT_PUBLIC_STORAGE_URL=${NEXT_PUBLIC_STORAGE_URL}
|
|
||||||
- NEXT_PUBLIC_AVATAR_BUCKET_NAME=${NEXT_PUBLIC_AVATAR_BUCKET_NAME}
|
|
||||||
- NEXT_PUBLIC_STORAGE_DOMAIN=${NEXT_PUBLIC_STORAGE_DOMAIN}
|
|
||||||
- NEXT_PUBLIC_UMAMI_ID=${NEXT_PUBLIC_UMAMI_ID}
|
- NEXT_PUBLIC_UMAMI_ID=${NEXT_PUBLIC_UMAMI_ID}
|
||||||
- NEXT_PUBLIC_ALLOW_CREDENTIALS=${NEXT_PUBLIC_ALLOW_CREDENTIALS}
|
- STRIPE_SECRET_KEY=${STRIPE_SECRET_KEY}
|
||||||
- NEXT_PUBLIC_DISABLE_SIGN_UP=${NEXT_PUBLIC_DISABLE_SIGN_UP}
|
- STRIPE_WEBHOOK_SECRET=${STRIPE_WEBHOOK_SECRET}
|
||||||
|
- NEXT_PUBLIC_POSTHOG_KEY=${NEXT_PUBLIC_POSTHOG_KEY}
|
||||||
|
- NEXT_PUBLIC_POSTHOG_HOST=${NEXT_PUBLIC_POSTHOG_HOST}
|
||||||
|
depends_on:
|
||||||
|
- postgres
|
||||||
|
restart: unless-stopped
|
||||||
|
|
||||||
|
postgres:
|
||||||
|
image: postgres:15
|
||||||
|
container_name: kan-db
|
||||||
|
environment:
|
||||||
|
- POSTGRES_DB=kan_db
|
||||||
|
- POSTGRES_USER=kan
|
||||||
|
- POSTGRES_PASSWORD=${POSTGRES_PASSWORD}
|
||||||
|
ports:
|
||||||
|
- 5432:5432
|
||||||
|
volumes:
|
||||||
|
- kan_postgres_data:/var/lib/postgresql/data
|
||||||
|
restart: unless-stopped
|
||||||
|
networks:
|
||||||
|
- kan-network
|
||||||
|
|
||||||
networks:
|
networks:
|
||||||
|
kan-network:
|
||||||
dokploy-network:
|
dokploy-network:
|
||||||
external: true
|
external: true
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
kan_postgres_data:
|
||||||
|
|||||||
@@ -29,22 +29,34 @@ export const sendEmail = async (
|
|||||||
template: Templates,
|
template: Templates,
|
||||||
data: Record<string, string>,
|
data: Record<string, string>,
|
||||||
) => {
|
) => {
|
||||||
const EmailTemplate = emailTemplates[template];
|
try {
|
||||||
|
const EmailTemplate = emailTemplates[template];
|
||||||
|
|
||||||
const html = await render(<EmailTemplate {...data} />, { pretty: true });
|
const html = await render(<EmailTemplate {...data} />, { pretty: true });
|
||||||
|
|
||||||
const options = {
|
const options = {
|
||||||
from: process.env.EMAIL_FROM,
|
from: process.env.EMAIL_FROM,
|
||||||
to,
|
to,
|
||||||
subject,
|
subject,
|
||||||
html,
|
html,
|
||||||
};
|
};
|
||||||
|
|
||||||
const response = await transporter.sendMail(options);
|
const response = await transporter.sendMail(options);
|
||||||
|
|
||||||
if (!response.accepted.length) {
|
if (!response.accepted.length) {
|
||||||
throw new Error(`Failed to send email: ${response.response}`);
|
throw new Error(`Failed to send email: ${response.response}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
return response;
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Email sending failed:", {
|
||||||
|
to,
|
||||||
|
from: process.env.EMAIL_FROM,
|
||||||
|
subject,
|
||||||
|
template,
|
||||||
|
error: error instanceof Error ? error.message : String(error),
|
||||||
|
stack: error instanceof Error ? error.stack : undefined,
|
||||||
|
});
|
||||||
|
throw error;
|
||||||
}
|
}
|
||||||
|
|
||||||
return response;
|
|
||||||
};
|
};
|
||||||
|
|||||||
41
pnpm-lock.yaml
generated
41
pnpm-lock.yaml
generated
@@ -159,6 +159,9 @@ importers:
|
|||||||
pino:
|
pino:
|
||||||
specifier: ^9.6.0
|
specifier: ^9.6.0
|
||||||
version: 9.6.0
|
version: 9.6.0
|
||||||
|
posthog-js:
|
||||||
|
specifier: ^1.254.0
|
||||||
|
version: 1.254.0
|
||||||
react:
|
react:
|
||||||
specifier: catalog:react18
|
specifier: catalog:react18
|
||||||
version: 18.3.1
|
version: 18.3.1
|
||||||
@@ -3064,6 +3067,9 @@ packages:
|
|||||||
core-js-pure@3.39.0:
|
core-js-pure@3.39.0:
|
||||||
resolution: {integrity: sha512-7fEcWwKI4rJinnK+wLTezeg2smbFFdSBP6E2kQZNbnzM2s1rpKQ6aaRteZSSg7FLU3P0HGGVo/gbpfanU36urg==}
|
resolution: {integrity: sha512-7fEcWwKI4rJinnK+wLTezeg2smbFFdSBP6E2kQZNbnzM2s1rpKQ6aaRteZSSg7FLU3P0HGGVo/gbpfanU36urg==}
|
||||||
|
|
||||||
|
core-js@3.43.0:
|
||||||
|
resolution: {integrity: sha512-N6wEbTTZSYOY2rYAn85CuvWWkCK6QweMn7/4Nr3w+gDBeBhk/x4EJeY6FPo4QzDoJZxVTv8U7CMvgWk6pOHHqA==}
|
||||||
|
|
||||||
cors@2.8.5:
|
cors@2.8.5:
|
||||||
resolution: {integrity: sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==}
|
resolution: {integrity: sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==}
|
||||||
engines: {node: '>= 0.10'}
|
engines: {node: '>= 0.10'}
|
||||||
@@ -3664,6 +3670,9 @@ packages:
|
|||||||
resolution: {integrity: sha512-k/2rVBRIRzOeom3wI9jBPaSEvoTSQEW4iM0EveBmBBKFxO8mSyyRWtDlfC3VnEfu0avmjrMzy8/ZFPSe6F71Hw==}
|
resolution: {integrity: sha512-k/2rVBRIRzOeom3wI9jBPaSEvoTSQEW4iM0EveBmBBKFxO8mSyyRWtDlfC3VnEfu0avmjrMzy8/ZFPSe6F71Hw==}
|
||||||
engines: {node: '>=14.0.0'}
|
engines: {node: '>=14.0.0'}
|
||||||
|
|
||||||
|
fflate@0.4.8:
|
||||||
|
resolution: {integrity: sha512-FJqqoDBR00Mdj9ppamLa/Y7vxm+PRmNWA67N846RvsoYVMKB4q3y/de5PA7gUmRMYK/8CMz2GDZQmCRN1wBcWA==}
|
||||||
|
|
||||||
figures@3.2.0:
|
figures@3.2.0:
|
||||||
resolution: {integrity: sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==}
|
resolution: {integrity: sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==}
|
||||||
engines: {node: '>=8'}
|
engines: {node: '>=8'}
|
||||||
@@ -5199,6 +5208,20 @@ packages:
|
|||||||
resolution: {integrity: sha512-cDWgoah1Gez9rN3H4165peY9qfpEo+SA61oQv65O3cRUE1pOEoJWwddwcqKE8XZYjbblOJlYDlLV4h67HrEVDg==}
|
resolution: {integrity: sha512-cDWgoah1Gez9rN3H4165peY9qfpEo+SA61oQv65O3cRUE1pOEoJWwddwcqKE8XZYjbblOJlYDlLV4h67HrEVDg==}
|
||||||
engines: {node: '>=12'}
|
engines: {node: '>=12'}
|
||||||
|
|
||||||
|
posthog-js@1.254.0:
|
||||||
|
resolution: {integrity: sha512-ISJEsYTIx8FJ0W78q2lfueSrrcuNKCjGfgk9LLuRhAfxRJ+N5ljyGreeEWVrkli2QJBAWje0qYUcregBcIs33A==}
|
||||||
|
peerDependencies:
|
||||||
|
'@rrweb/types': 2.0.0-alpha.17
|
||||||
|
rrweb-snapshot: 2.0.0-alpha.17
|
||||||
|
peerDependenciesMeta:
|
||||||
|
'@rrweb/types':
|
||||||
|
optional: true
|
||||||
|
rrweb-snapshot:
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
preact@10.26.9:
|
||||||
|
resolution: {integrity: sha512-SSjF9vcnF27mJK1XyFMNJzFd5u3pQiATFqoaDy03XuN00u4ziveVVEGt5RKJrDR8MHE/wJo9Nnad56RLzS2RMA==}
|
||||||
|
|
||||||
prelude-ls@1.2.1:
|
prelude-ls@1.2.1:
|
||||||
resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==}
|
resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==}
|
||||||
engines: {node: '>= 0.8.0'}
|
engines: {node: '>= 0.8.0'}
|
||||||
@@ -6250,6 +6273,9 @@ packages:
|
|||||||
wcwidth@1.0.1:
|
wcwidth@1.0.1:
|
||||||
resolution: {integrity: sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==}
|
resolution: {integrity: sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==}
|
||||||
|
|
||||||
|
web-vitals@4.2.4:
|
||||||
|
resolution: {integrity: sha512-r4DIlprAGwJ7YM11VZp4R884m0Vmgr6EAKe3P+kO0PPj3Unqyvv59rczf6UiGcb9Z8QxZVcqKNwv/g0WNdWwsw==}
|
||||||
|
|
||||||
webidl-conversions@3.0.1:
|
webidl-conversions@3.0.1:
|
||||||
resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==}
|
resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==}
|
||||||
|
|
||||||
@@ -9375,6 +9401,8 @@ snapshots:
|
|||||||
|
|
||||||
core-js-pure@3.39.0: {}
|
core-js-pure@3.39.0: {}
|
||||||
|
|
||||||
|
core-js@3.43.0: {}
|
||||||
|
|
||||||
cors@2.8.5:
|
cors@2.8.5:
|
||||||
dependencies:
|
dependencies:
|
||||||
object-assign: 4.1.1
|
object-assign: 4.1.1
|
||||||
@@ -10079,6 +10107,8 @@ snapshots:
|
|||||||
sharp: 0.33.5
|
sharp: 0.33.5
|
||||||
xml2js: 0.6.2
|
xml2js: 0.6.2
|
||||||
|
|
||||||
|
fflate@0.4.8: {}
|
||||||
|
|
||||||
figures@3.2.0:
|
figures@3.2.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
escape-string-regexp: 1.0.5
|
escape-string-regexp: 1.0.5
|
||||||
@@ -12078,6 +12108,15 @@ snapshots:
|
|||||||
postgres@3.4.5:
|
postgres@3.4.5:
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
|
posthog-js@1.254.0:
|
||||||
|
dependencies:
|
||||||
|
core-js: 3.43.0
|
||||||
|
fflate: 0.4.8
|
||||||
|
preact: 10.26.9
|
||||||
|
web-vitals: 4.2.4
|
||||||
|
|
||||||
|
preact@10.26.9: {}
|
||||||
|
|
||||||
prelude-ls@1.2.1: {}
|
prelude-ls@1.2.1: {}
|
||||||
|
|
||||||
prettier-plugin-tailwindcss@0.6.9(@ianvs/prettier-plugin-sort-imports@4.4.0(prettier@3.4.2))(prettier@3.4.2):
|
prettier-plugin-tailwindcss@0.6.9(@ianvs/prettier-plugin-sort-imports@4.4.0(prettier@3.4.2))(prettier@3.4.2):
|
||||||
@@ -13320,6 +13359,8 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
defaults: 1.0.4
|
defaults: 1.0.4
|
||||||
|
|
||||||
|
web-vitals@4.2.4: {}
|
||||||
|
|
||||||
webidl-conversions@3.0.1: {}
|
webidl-conversions@3.0.1: {}
|
||||||
|
|
||||||
whatwg-url@5.0.0:
|
whatwg-url@5.0.0:
|
||||||
|
|||||||
@@ -100,6 +100,7 @@
|
|||||||
"NEXT_PUBLIC_AVATAR_BUCKET_NAME",
|
"NEXT_PUBLIC_AVATAR_BUCKET_NAME",
|
||||||
"NEXT_PUBLIC_ALLOW_CREDENTIALS",
|
"NEXT_PUBLIC_ALLOW_CREDENTIALS",
|
||||||
"NEXT_PUBLIC_DISABLE_SIGN_UP",
|
"NEXT_PUBLIC_DISABLE_SIGN_UP",
|
||||||
|
"NEXT_PUBLIC_USE_STANDALONE_OUTPUT",
|
||||||
"S3_REGION",
|
"S3_REGION",
|
||||||
"S3_ACCESS_KEY_ID",
|
"S3_ACCESS_KEY_ID",
|
||||||
"S3_SECRET_ACCESS_KEY",
|
"S3_SECRET_ACCESS_KEY",
|
||||||
|
|||||||
Reference in New Issue
Block a user