Compare commits
1 Commits
chore/impr
...
fix/openap
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fb85a44676 |
@@ -21,7 +21,6 @@ S3_REGION=
|
|||||||
S3_ENDPOINT=
|
S3_ENDPOINT=
|
||||||
S3_ACCESS_KEY_ID=
|
S3_ACCESS_KEY_ID=
|
||||||
S3_SECRET_ACCESS_KEY=
|
S3_SECRET_ACCESS_KEY=
|
||||||
S3_FORCE_PATH_STYLE=
|
|
||||||
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=
|
||||||
|
|||||||
22
CHANGELOG.md
22
CHANGELOG.md
@@ -5,27 +5,7 @@ All notable changes to this project will be documented in this file.
|
|||||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||||
|
|
||||||
## [Unreleased](https://github.com/kanbn/kan/compare/v0.2.3...HEAD)
|
## [Unreleased](https://github.com/kanbn/kan/compare/v0.2.2...HEAD)
|
||||||
|
|
||||||
## [0.2.3](https://github.com/kanbn/kan/compare/v0.2.2...v0.2.3) - 2025-07-07
|
|
||||||
|
|
||||||
### Added
|
|
||||||
|
|
||||||
- Mobile support with responsive navigation and layout
|
|
||||||
- Template selection when creating new boards with predefined lists and labels
|
|
||||||
- S3_FORCE_PATH_STYLE configuration for MinIO compatibility
|
|
||||||
|
|
||||||
### Changed
|
|
||||||
|
|
||||||
- Improved mobile UI styling and layout containers
|
|
||||||
- Enhanced iOS mobile support with viewport fixes and zoom prevention
|
|
||||||
- Improved card view layout for mobile devices
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
|
|
||||||
- Mobile dashboard min-width issues
|
|
||||||
- Dynamic viewport height on mobile devices
|
|
||||||
- Scroll functionality on card pages
|
|
||||||
|
|
||||||
## [0.2.2](https://github.com/kanbn/kan/compare/v0.2.1...v0.2.2) - 2025-06-28
|
## [0.2.2](https://github.com/kanbn/kan/compare/v0.2.1...v0.2.2) - 2025-06-28
|
||||||
|
|
||||||
|
|||||||
@@ -162,7 +162,6 @@ pnpm dev
|
|||||||
| `S3_ENDPOINT` | S3 endpoint URL | For file uploads | `https://xxx.r2.cloudflarestorage.com` |
|
| `S3_ENDPOINT` | S3 endpoint URL | For file uploads | `https://xxx.r2.cloudflarestorage.com` |
|
||||||
| `S3_ACCESS_KEY_ID` | S3 access key | For file uploads | `xxx` |
|
| `S3_ACCESS_KEY_ID` | S3 access key | For file uploads | `xxx` |
|
||||||
| `S3_SECRET_ACCESS_KEY` | S3 secret key | For file uploads | `xxx` |
|
| `S3_SECRET_ACCESS_KEY` | S3 secret key | For file uploads | `xxx` |
|
||||||
| `S3_FORCE_PATH_STYLE` | Use path-style URLs for S3 | For file uploads | `true` |
|
|
||||||
| `NEXT_PUBLIC_STORAGE_URL` | Storage service URL | For file uploads | `https://storage.kanbn.com` |
|
| `NEXT_PUBLIC_STORAGE_URL` | Storage service URL | For file uploads | `https://storage.kanbn.com` |
|
||||||
| `NEXT_PUBLIC_STORAGE_DOMAIN` | Storage domain name | For file uploads | `kanbn.com` |
|
| `NEXT_PUBLIC_STORAGE_DOMAIN` | Storage domain name | For file uploads | `kanbn.com` |
|
||||||
| `NEXT_PUBLIC_AVATAR_BUCKET_NAME` | S3 bucket name for avatars | For file uploads | `avatars` |
|
| `NEXT_PUBLIC_AVATAR_BUCKET_NAME` | S3 bucket name for avatars | For file uploads | `avatars` |
|
||||||
|
|||||||
@@ -31,12 +31,9 @@ const config = {
|
|||||||
|
|
||||||
images: {
|
images: {
|
||||||
remotePatterns: [
|
remotePatterns: [
|
||||||
|
|
||||||
{
|
{
|
||||||
protocol: "https",
|
protocol: "https",
|
||||||
hostname: process.env.S3_FORCE_PATH_STYLE === "true"
|
hostname: `*.${env("NEXT_PUBLIC_STORAGE_DOMAIN")}`,
|
||||||
? `${env("NEXT_PUBLIC_STORAGE_DOMAIN")}`
|
|
||||||
: `*.${env("NEXT_PUBLIC_STORAGE_DOMAIN")}`,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
protocol: "http",
|
protocol: "http",
|
||||||
|
|||||||
@@ -10,7 +10,6 @@ import {
|
|||||||
import { authClient } from "@kan/auth/client";
|
import { authClient } from "@kan/auth/client";
|
||||||
|
|
||||||
import { useClickOutside } from "~/hooks/useClickOutside";
|
import { useClickOutside } from "~/hooks/useClickOutside";
|
||||||
import { useTheme } from "~/providers/theme";
|
|
||||||
import FeedbackButton from "./FeedbackButton";
|
import FeedbackButton from "./FeedbackButton";
|
||||||
import SideNavigation from "./SideNavigation";
|
import SideNavigation from "./SideNavigation";
|
||||||
|
|
||||||
@@ -25,8 +24,6 @@ export default function Dashboard({
|
|||||||
rightPanel,
|
rightPanel,
|
||||||
hasRightPanel = false,
|
hasRightPanel = false,
|
||||||
}: DashboardProps) {
|
}: DashboardProps) {
|
||||||
const theme = useTheme();
|
|
||||||
|
|
||||||
const { data: session, isPending: sessionLoading } = authClient.useSession();
|
const { data: session, isPending: sessionLoading } = authClient.useSession();
|
||||||
const [isSideNavOpen, setIsSideNavOpen] = useState(false);
|
const [isSideNavOpen, setIsSideNavOpen] = useState(false);
|
||||||
const [isRightPanelOpen, setIsRightPanelOpen] = useState(false);
|
const [isRightPanelOpen, setIsRightPanelOpen] = useState(false);
|
||||||
@@ -68,20 +65,16 @@ export default function Dashboard({
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const isDarkMode = theme.activeTheme === "dark";
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<style jsx global>{`
|
<style jsx global>{`
|
||||||
html {
|
html {
|
||||||
height: 100vh;
|
height: 100vh;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
min-width: 320px;
|
|
||||||
background-color: ${!isDarkMode ? "hsl(0deg 0% 97.3%)" : "#161616"};
|
|
||||||
}
|
}
|
||||||
`}</style>
|
`}</style>
|
||||||
<div className="flex h-screen flex-col items-center bg-light-100 dark:bg-dark-50 md:min-w-[800px]">
|
<div className="h-screen flex-col items-center bg-light-100 dark:bg-dark-50 md:flex md:min-w-[800px]">
|
||||||
<div className="flex h-12 min-h-12 w-full justify-between border-b border-light-600 px-5 py-2 align-middle dark:border-dark-400 md:h-16 md:min-h-16">
|
<div className="m-auto flex h-12 min-h-12 w-full justify-between border-b border-light-600 px-5 py-2 align-middle dark:border-dark-400 md:h-16 md:min-h-16">
|
||||||
<div className="my-auto flex w-full items-center justify-between">
|
<div className="my-auto flex w-full items-center justify-between">
|
||||||
<button
|
<button
|
||||||
ref={sideNavButtonRef}
|
ref={sideNavButtonRef}
|
||||||
@@ -134,10 +127,10 @@ export default function Dashboard({
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex h-full min-h-0 w-full">
|
<div className="flex h-full w-full">
|
||||||
<div
|
<div
|
||||||
ref={sideNavRef}
|
ref={sideNavRef}
|
||||||
className={`fixed left-0 top-12 z-50 h-[calc(100dvh-3rem)] transform transition-transform duration-300 ease-in-out md:relative md:top-0 md:h-full md:translate-x-0 ${isSideNavOpen ? "translate-x-0" : "-translate-x-full md:translate-x-0"} `}
|
className={`fixed left-0 z-50 h-[calc(100vh-3rem)] transform transition-transform duration-300 ease-in-out md:relative md:h-[calc(100vh-4rem)] md:translate-x-0 ${isSideNavOpen ? "translate-x-0" : "-translate-x-full md:translate-x-0"} `}
|
||||||
>
|
>
|
||||||
<SideNavigation
|
<SideNavigation
|
||||||
user={{ email: session?.user.email, image: session?.user.image }}
|
user={{ email: session?.user.email, image: session?.user.image }}
|
||||||
@@ -145,14 +138,14 @@ export default function Dashboard({
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="relative flex min-h-0 w-full overflow-hidden">
|
<div className="relative flex w-full overflow-hidden">
|
||||||
<div className="h-full w-full overflow-y-auto">{children}</div>
|
<div className="w-full overflow-hidden">{children}</div>
|
||||||
|
|
||||||
{/* Mobile Right Panel */}
|
{/* Mobile Right Panel */}
|
||||||
{hasRightPanel && rightPanel && (
|
{hasRightPanel && rightPanel && (
|
||||||
<div
|
<div
|
||||||
ref={rightPanelRef}
|
ref={rightPanelRef}
|
||||||
className={`fixed right-0 top-12 z-50 h-[calc(100dvh-3rem)] w-80 transform bg-light-200 transition-transform duration-300 ease-in-out dark:bg-dark-100 md:hidden ${
|
className={`fixed right-0 top-12 z-50 h-[calc(100vh-3rem)] w-80 transform bg-light-200 transition-transform duration-300 ease-in-out dark:bg-dark-100 md:hidden ${
|
||||||
isRightPanelOpen ? "translate-x-0" : "translate-x-full"
|
isRightPanelOpen ? "translate-x-0" : "translate-x-full"
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -4,10 +4,6 @@ export const PageHead = ({ title }: { title: string }) => {
|
|||||||
return (
|
return (
|
||||||
<Head>
|
<Head>
|
||||||
<title>{title}</title>
|
<title>{title}</title>
|
||||||
<meta
|
|
||||||
name="viewport"
|
|
||||||
content="width=device-width, initial-scale=1, maximum-scale=1"
|
|
||||||
/>
|
|
||||||
</Head>
|
</Head>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -69,7 +69,6 @@ export const env = createEnv({
|
|||||||
S3_SECRET_ACCESS_KEY: z.string().optional(),
|
S3_SECRET_ACCESS_KEY: z.string().optional(),
|
||||||
S3_REGION: z.string().optional(),
|
S3_REGION: z.string().optional(),
|
||||||
S3_ENDPOINT: z.string().optional(),
|
S3_ENDPOINT: z.string().optional(),
|
||||||
S3_FORCE_PATH_STYLE: z.string().optional(),
|
|
||||||
EMAIL_FROM: z.string().optional(),
|
EMAIL_FROM: z.string().optional(),
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|||||||
@@ -46,7 +46,6 @@ export default async function handler(
|
|||||||
const client = new S3Client({
|
const client = new S3Client({
|
||||||
region: env.S3_REGION ?? "",
|
region: env.S3_REGION ?? "",
|
||||||
endpoint: env.S3_ENDPOINT ?? "",
|
endpoint: env.S3_ENDPOINT ?? "",
|
||||||
forcePathStyle: env.S3_FORCE_PATH_STYLE === "true",
|
|
||||||
credentials: {
|
credentials: {
|
||||||
accessKeyId: env.S3_ACCESS_KEY_ID ?? "",
|
accessKeyId: env.S3_ACCESS_KEY_ID ?? "",
|
||||||
secretAccessKey: env.S3_SECRET_ACCESS_KEY ?? "",
|
secretAccessKey: env.S3_SECRET_ACCESS_KEY ?? "",
|
||||||
@@ -59,7 +58,6 @@ export default async function handler(
|
|||||||
new PutObjectCommand({
|
new PutObjectCommand({
|
||||||
Bucket: nextRuntimeEnv("NEXT_PUBLIC_AVATAR_BUCKET_NAME") ?? "",
|
Bucket: nextRuntimeEnv("NEXT_PUBLIC_AVATAR_BUCKET_NAME") ?? "",
|
||||||
Key: filename,
|
Key: filename,
|
||||||
ACL: "public-read",
|
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -189,7 +189,7 @@ export default function CardPage() {
|
|||||||
/>
|
/>
|
||||||
<div className="flex h-full flex-1 flex-row">
|
<div className="flex h-full flex-1 flex-row">
|
||||||
<div className="flex h-full w-full flex-col overflow-hidden">
|
<div className="flex h-full w-full flex-col overflow-hidden">
|
||||||
<div className="h-full max-h-[calc(100dvh-3rem)] overflow-y-auto p-6 md:max-h-[calc(100dvh-4rem)] md:p-8">
|
<div className="h-full max-h-[calc(100vh-4rem)] overflow-y-auto p-6 md:p-8">
|
||||||
<div className="mb-8 flex w-full items-center justify-between">
|
<div className="mb-8 flex w-full items-center justify-between">
|
||||||
{!card && isLoading && (
|
{!card && isLoading && (
|
||||||
<div className="flex space-x-2">
|
<div className="flex space-x-2">
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ export default function HomeView() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* <div className="relative overflow-hidden rounded-full bg-gradient-to-b from-light-300 to-light-400 p-[2px] dark:from-dark-300 dark:to-dark-400">
|
<div className="relative overflow-hidden rounded-full bg-gradient-to-b from-light-300 to-light-400 p-[2px] dark:from-dark-300 dark:to-dark-400">
|
||||||
<div className="relative z-10 rounded-full bg-light-50 dark:bg-dark-50">
|
<div className="relative z-10 rounded-full bg-light-50 dark:bg-dark-50">
|
||||||
<Link
|
<Link
|
||||||
href="https://news.ycombinator.com/item?id=44157177"
|
href="https://news.ycombinator.com/item?id=44157177"
|
||||||
@@ -58,7 +58,7 @@ export default function HomeView() {
|
|||||||
</div>
|
</div>
|
||||||
</Link>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
</div> */}
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<p className="mt-2 text-center text-4xl font-bold text-light-1000 dark:text-dark-1000 lg:text-5xl">
|
<p className="mt-2 text-center text-4xl font-bold text-light-1000 dark:text-dark-1000 lg:text-5xl">
|
||||||
|
|||||||
@@ -104,7 +104,7 @@ export default function SettingsPage() {
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className="flex h-full w-full flex-col overflow-hidden">
|
<div className="flex h-full w-full flex-col overflow-hidden">
|
||||||
<div className="h-full max-h-[calc(100vdh-3rem)] overflow-y-auto md:max-h-[calc(100vdh-4rem)]">
|
<div className="h-full max-h-[calc(100vh-4rem)] overflow-y-auto">
|
||||||
<PageHead title={t`Settings | ${workspace.name ?? "Workspace"}`} />
|
<PageHead title={t`Settings | ${workspace.name ?? "Workspace"}`} />
|
||||||
<div className="m-auto max-w-[1600px] px-5 py-6 md:px-28 md:py-12">
|
<div className="m-auto max-w-[1600px] px-5 py-6 md:px-28 md:py-12">
|
||||||
<div className="mb-8 flex w-full justify-between">
|
<div className="mb-8 flex w-full justify-between">
|
||||||
|
|||||||
@@ -36,7 +36,6 @@ services:
|
|||||||
- S3_SECRET_ACCESS_KEY=${S3_SECRET_ACCESS_KEY}
|
- S3_SECRET_ACCESS_KEY=${S3_SECRET_ACCESS_KEY}
|
||||||
- S3_REGION=${S3_REGION}
|
- S3_REGION=${S3_REGION}
|
||||||
- S3_ENDPOINT=${S3_ENDPOINT}
|
- S3_ENDPOINT=${S3_ENDPOINT}
|
||||||
- S3_FORCE_PATH_STYLE=${S3_FORCE_PATH_STYLE}
|
|
||||||
- NEXT_PUBLIC_STORAGE_URL=${NEXT_PUBLIC_STORAGE_URL}
|
- NEXT_PUBLIC_STORAGE_URL=${NEXT_PUBLIC_STORAGE_URL}
|
||||||
- NEXT_PUBLIC_AVATAR_BUCKET_NAME=${NEXT_PUBLIC_AVATAR_BUCKET_NAME}
|
- NEXT_PUBLIC_AVATAR_BUCKET_NAME=${NEXT_PUBLIC_AVATAR_BUCKET_NAME}
|
||||||
- NEXT_PUBLIC_STORAGE_DOMAIN=${NEXT_PUBLIC_STORAGE_DOMAIN}
|
- NEXT_PUBLIC_STORAGE_DOMAIN=${NEXT_PUBLIC_STORAGE_DOMAIN}
|
||||||
|
|||||||
@@ -30,7 +30,6 @@ services:
|
|||||||
- S3_SECRET_ACCESS_KEY=${S3_SECRET_ACCESS_KEY}
|
- S3_SECRET_ACCESS_KEY=${S3_SECRET_ACCESS_KEY}
|
||||||
- S3_REGION=${S3_REGION}
|
- S3_REGION=${S3_REGION}
|
||||||
- S3_ENDPOINT=${S3_ENDPOINT}
|
- S3_ENDPOINT=${S3_ENDPOINT}
|
||||||
- S3_FORCE_PATH_STYLE=${S3_FORCE_PATH_STYLE}
|
|
||||||
- NEXT_PUBLIC_STORAGE_URL=${NEXT_PUBLIC_STORAGE_URL}
|
- NEXT_PUBLIC_STORAGE_URL=${NEXT_PUBLIC_STORAGE_URL}
|
||||||
- NEXT_PUBLIC_AVATAR_BUCKET_NAME=${NEXT_PUBLIC_AVATAR_BUCKET_NAME}
|
- NEXT_PUBLIC_AVATAR_BUCKET_NAME=${NEXT_PUBLIC_AVATAR_BUCKET_NAME}
|
||||||
- NEXT_PUBLIC_STORAGE_DOMAIN=${NEXT_PUBLIC_STORAGE_DOMAIN}
|
- NEXT_PUBLIC_STORAGE_DOMAIN=${NEXT_PUBLIC_STORAGE_DOMAIN}
|
||||||
|
|||||||
@@ -8,12 +8,6 @@ import * as labelRepo from "@kan/db/repository/label.repo";
|
|||||||
import { createTRPCRouter, protectedProcedure } from "../trpc";
|
import { createTRPCRouter, protectedProcedure } from "../trpc";
|
||||||
import { assertUserInWorkspace } from "../utils/auth";
|
import { assertUserInWorkspace } from "../utils/auth";
|
||||||
|
|
||||||
const labelSchema = z.object({
|
|
||||||
publicId: z.string(),
|
|
||||||
name: z.string(),
|
|
||||||
colourCode: z.string().nullable(),
|
|
||||||
});
|
|
||||||
|
|
||||||
export const labelRouter = createTRPCRouter({
|
export const labelRouter = createTRPCRouter({
|
||||||
byPublicId: protectedProcedure
|
byPublicId: protectedProcedure
|
||||||
.meta({
|
.meta({
|
||||||
@@ -27,7 +21,7 @@ export const labelRouter = createTRPCRouter({
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
.input(z.object({ labelPublicId: z.string().min(12) }))
|
.input(z.object({ labelPublicId: z.string().min(12) }))
|
||||||
.output(labelSchema)
|
.output(z.custom<Awaited<ReturnType<typeof labelRepo.getByPublicId>>>())
|
||||||
.query(async ({ ctx, input }) => {
|
.query(async ({ ctx, input }) => {
|
||||||
const userId = ctx.user?.id;
|
const userId = ctx.user?.id;
|
||||||
|
|
||||||
@@ -58,11 +52,7 @@ export const labelRouter = createTRPCRouter({
|
|||||||
code: "NOT_FOUND",
|
code: "NOT_FOUND",
|
||||||
});
|
});
|
||||||
|
|
||||||
return {
|
return result;
|
||||||
publicId: result.publicId,
|
|
||||||
name: result.name,
|
|
||||||
colourCode: result.colourCode,
|
|
||||||
};
|
|
||||||
}),
|
}),
|
||||||
create: protectedProcedure
|
create: protectedProcedure
|
||||||
.meta({
|
.meta({
|
||||||
@@ -82,7 +72,7 @@ export const labelRouter = createTRPCRouter({
|
|||||||
colourCode: z.string().length(7),
|
colourCode: z.string().length(7),
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
.output(labelSchema)
|
.output(z.custom<Awaited<ReturnType<typeof labelRepo.create>>>())
|
||||||
.mutation(async ({ ctx, input }) => {
|
.mutation(async ({ ctx, input }) => {
|
||||||
const userId = ctx.user?.id;
|
const userId = ctx.user?.id;
|
||||||
|
|
||||||
@@ -118,11 +108,7 @@ export const labelRouter = createTRPCRouter({
|
|||||||
code: "INTERNAL_SERVER_ERROR",
|
code: "INTERNAL_SERVER_ERROR",
|
||||||
});
|
});
|
||||||
|
|
||||||
return {
|
return result;
|
||||||
publicId: result.publicId,
|
|
||||||
name: result.name,
|
|
||||||
colourCode: result.colourCode,
|
|
||||||
};
|
|
||||||
}),
|
}),
|
||||||
update: protectedProcedure
|
update: protectedProcedure
|
||||||
.meta({
|
.meta({
|
||||||
@@ -142,7 +128,7 @@ export const labelRouter = createTRPCRouter({
|
|||||||
colourCode: z.string().length(7),
|
colourCode: z.string().length(7),
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
.output(labelSchema)
|
.output(z.custom<Awaited<ReturnType<typeof labelRepo.update>>>())
|
||||||
.mutation(async ({ ctx, input }) => {
|
.mutation(async ({ ctx, input }) => {
|
||||||
const userId = ctx.user?.id;
|
const userId = ctx.user?.id;
|
||||||
|
|
||||||
@@ -167,17 +153,7 @@ export const labelRouter = createTRPCRouter({
|
|||||||
|
|
||||||
const result = await labelRepo.update(ctx.db, input);
|
const result = await labelRepo.update(ctx.db, input);
|
||||||
|
|
||||||
if (!result)
|
return result;
|
||||||
throw new TRPCError({
|
|
||||||
message: `Failed to update label`,
|
|
||||||
code: "INTERNAL_SERVER_ERROR",
|
|
||||||
});
|
|
||||||
|
|
||||||
return {
|
|
||||||
publicId: result.publicId,
|
|
||||||
name: result.name,
|
|
||||||
colourCode: result.colourCode,
|
|
||||||
};
|
|
||||||
}),
|
}),
|
||||||
delete: protectedProcedure
|
delete: protectedProcedure
|
||||||
.meta({
|
.meta({
|
||||||
|
|||||||
@@ -83,18 +83,11 @@ export const memberRouter = createTRPCRouter({
|
|||||||
callbackURL: `/boards?type=invite&memberPublicId=${invite.publicId}`,
|
callbackURL: `/boards?type=invite&memberPublicId=${invite.publicId}`,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (error) {
|
if (error)
|
||||||
console.error("Failed to send magic link invitation:", {
|
|
||||||
email: input.email,
|
|
||||||
callbackURL: `/boards?type=invite&memberPublicId=${invite.publicId}`,
|
|
||||||
error,
|
|
||||||
});
|
|
||||||
|
|
||||||
throw new TRPCError({
|
throw new TRPCError({
|
||||||
message: `Failed to send magic link invitation to user with email ${input.email}.`,
|
message: `Failed to send magic link to user with email ${input.email}`,
|
||||||
code: "INTERNAL_SERVER_ERROR",
|
code: "INTERNAL_SERVER_ERROR",
|
||||||
});
|
});
|
||||||
}
|
|
||||||
|
|
||||||
return invite;
|
return invite;
|
||||||
}),
|
}),
|
||||||
|
|||||||
@@ -181,7 +181,6 @@ export const initAuth = (db: dbClient) => {
|
|||||||
const client = new S3Client({
|
const client = new S3Client({
|
||||||
region: env("S3_REGION") ?? "",
|
region: env("S3_REGION") ?? "",
|
||||||
endpoint: env("S3_ENDPOINT") ?? "",
|
endpoint: env("S3_ENDPOINT") ?? "",
|
||||||
forcePathStyle: env("S3_FORCE_PATH_STYLE") === "true",
|
|
||||||
credentials: {
|
credentials: {
|
||||||
accessKeyId: env("S3_ACCESS_KEY_ID") ?? "",
|
accessKeyId: env("S3_ACCESS_KEY_ID") ?? "",
|
||||||
secretAccessKey: env("S3_SECRET_ACCESS_KEY") ?? "",
|
secretAccessKey: env("S3_SECRET_ACCESS_KEY") ?? "",
|
||||||
|
|||||||
@@ -23,12 +23,7 @@ export const create = async (
|
|||||||
createdBy: labelInput.createdBy,
|
createdBy: labelInput.createdBy,
|
||||||
boardId: labelInput.boardId,
|
boardId: labelInput.boardId,
|
||||||
})
|
})
|
||||||
.returning({
|
.returning({ id: labels.id });
|
||||||
id: labels.id,
|
|
||||||
publicId: labels.publicId,
|
|
||||||
name: labels.name,
|
|
||||||
colourCode: labels.colourCode,
|
|
||||||
});
|
|
||||||
|
|
||||||
if (labelInput.cardId && result) {
|
if (labelInput.cardId && result) {
|
||||||
await db.insert(cardsToLabels).values({
|
await db.insert(cardsToLabels).values({
|
||||||
@@ -96,7 +91,6 @@ export const update = async (
|
|||||||
.where(eq(labels.publicId, labelInput.labelPublicId))
|
.where(eq(labels.publicId, labelInput.labelPublicId))
|
||||||
.returning({
|
.returning({
|
||||||
id: labels.id,
|
id: labels.id,
|
||||||
publicId: labels.publicId,
|
|
||||||
name: labels.name,
|
name: labels.name,
|
||||||
colourCode: labels.colourCode,
|
colourCode: labels.colourCode,
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -106,7 +106,6 @@
|
|||||||
"S3_ACCESS_KEY_ID",
|
"S3_ACCESS_KEY_ID",
|
||||||
"S3_SECRET_ACCESS_KEY",
|
"S3_SECRET_ACCESS_KEY",
|
||||||
"S3_ENDPOINT",
|
"S3_ENDPOINT",
|
||||||
"S3_FORCE_PATH_STYLE",
|
|
||||||
"PORT",
|
"PORT",
|
||||||
"BETTER_AUTH_SECRET",
|
"BETTER_AUTH_SECRET",
|
||||||
"BETTER_AUTH_TRUSTED_ORIGINS"
|
"BETTER_AUTH_TRUSTED_ORIGINS"
|
||||||
|
|||||||
Reference in New Issue
Block a user