643f2de29b
Multi-tenant invite system without SMTP dependency. Designed for dev/early
stage; promotes to email-driven later by adding SMTP to Appwrite.
New schema:
- invite_links table (code, email, role, status, expiresAt, invitedBy)
with unique index on code, indexes on (tenantId,status) and (tenantId,email)
New code:
- lib/appwrite/audit.ts: logAudit() helper writes to audit_logs with
X-Forwarded-For/User-Agent capture; never throws.
- lib/appwrite/tenant-guard.ts: requireTenant() returns
{ user, tenantId, role, settings }; pulls highest role from team
memberships. requireRole() guard.
- lib/appwrite/team-actions.ts:
* inviteMemberAction — creates short code (8 char nanoid-style),
inserts invite_links row with team-scoped perms, returns shortUrl.
Reuses existing pending invite for same email instead of duplicating.
Blocks self-invite, blocks invite of existing members.
* cancelInviteAction — owner/admin only, marks status=cancelled.
* removeMemberAction — owner/admin only; protects self-removal and
requires owner-on-owner.
* updateMemberRoleAction — owner only.
* resolveInviteCode — public-ish lookup by code (admin SDK).
* acceptInviteAction — verifies session.email matches invite.email,
creates membership via admin SDK, marks invite accepted.
All mutations write to audit_logs.
UI:
- /d/[code] short-URL accept page (server). Logged-in matching user
sees 'Daveti kabul et' button; non-matching user sees error; logged-out
user gets sign-up / sign-in CTAs that preserve the code.
- /settings/members page (server): InviteForm, PendingInvitesTable,
MembersTable. Owner/admin gates respected; only owner can change roles.
- Sign-up and sign-in forms accept ?invite=CODE (and ?email= for sign-up):
hidden input -> server action redirects to /d/CODE on success.
Other:
- next.config.ts: removed eslint config block (deprecated in Next 16);
kept typescript.ignoreBuildErrors for template legacy.
63 lines
1.2 KiB
TypeScript
63 lines
1.2 KiB
TypeScript
import type { NextConfig } from "next";
|
|
|
|
const nextConfig: NextConfig = {
|
|
experimental: {
|
|
optimizePackageImports: ["lucide-react", "@radix-ui/react-icons"],
|
|
},
|
|
turbopack: {},
|
|
|
|
// TODO: re-enable once template files (chart.tsx, data-table-toolbar.tsx) are cleaned up.
|
|
typescript: { ignoreBuildErrors: true },
|
|
|
|
// Image optimization
|
|
images: {
|
|
remotePatterns: [
|
|
{
|
|
protocol: 'https',
|
|
hostname: 'ui.shadcn.com',
|
|
},
|
|
{
|
|
protocol: 'https',
|
|
hostname: 'images.unsplash.com',
|
|
},
|
|
],
|
|
formats: ['image/webp', 'image/avif'],
|
|
},
|
|
|
|
// Headers for better security and performance
|
|
async headers() {
|
|
return [
|
|
{
|
|
source: '/(.*)',
|
|
headers: [
|
|
{
|
|
key: 'X-Frame-Options',
|
|
value: 'DENY',
|
|
},
|
|
{
|
|
key: 'X-Content-Type-Options',
|
|
value: 'nosniff',
|
|
},
|
|
{
|
|
key: 'Referrer-Policy',
|
|
value: 'origin-when-cross-origin',
|
|
},
|
|
],
|
|
},
|
|
];
|
|
},
|
|
|
|
// Redirects for better SEO
|
|
async redirects() {
|
|
return [
|
|
{
|
|
source: '/home',
|
|
destination: '/dashboard',
|
|
permanent: true,
|
|
},
|
|
];
|
|
},
|
|
};
|
|
|
|
export default nextConfig;
|