FRE-4529: Transfer ShieldAI code from FrenoCorp repo

Transferred ShieldAI-related files mistakenly placed in ~/code/FrenoCorp:
- Services: spamshield (feature-flags, audit-logger, error-handler), voiceprint (config, service, feature-flags), darkwatch (pipeline, scan, scheduler, watchlist, webhook)
- Packages: shared-analytics, shared-auth, shared-ui, shared-utils (new); shared-billing, jobs supplemented with unique FC files
- Server: alerts (FC version newer), routes (spamshield, darkwatch, voiceprint)
- Config: turbo.json, tsconfig.base.json, vite/vitest configs, drizzle, Dockerfile
- VoicePrint ML service
- Examples

Pending: apps/{api,web,mobile}/ structured merge, shared-db/db mapping

Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
2026-05-02 10:13:13 -04:00
parent 8687868632
commit 1e42c4a5c2
45 changed files with 4837 additions and 562 deletions

View File

@@ -0,0 +1,114 @@
import { NextAuthOptions } from 'next-auth';
import CredentialsProvider from 'next-auth/providers/credentials';
import GoogleProvider from 'next-auth/providers/google';
import AppleProvider from 'next-auth/providers/apple';
import { z } from 'zod';
// Environment variables
const envSchema = z.object({
NEXTAUTH_URL: z.string().url(),
NEXTAUTH_SECRET: z.string().min(32),
GOOGLE_CLIENT_ID: z.string(),
GOOGLE_CLIENT_SECRET: z.string(),
APPLE_CLIENT_ID: z.string(),
APPLE_CLIENT_SECRET: z.string(),
DATABASE_URL: z.string().url(),
});
export const authEnv = envSchema.parse({
NEXTAUTH_URL: process.env.NEXTAUTH_URL,
NEXTAUTH_SECRET: process.env.NEXTAUTH_SECRET,
GOOGLE_CLIENT_ID: process.env.GOOGLE_CLIENT_ID,
GOOGLE_CLIENT_SECRET: process.env.GOOGLE_CLIENT_SECRET,
APPLE_CLIENT_ID: process.env.APPLE_CLIENT_ID,
APPLE_CLIENT_SECRET: process.env.APPLE_CLIENT_SECRET,
DATABASE_URL: process.env.DATABASE_URL,
});
// Role-based access control
export type UserRole = 'user' | 'family_admin' | 'family_member' | 'support';
export const userRoles: UserRole[] = ['user', 'family_admin', 'family_member', 'support'];
// Family group types
export type FamilyGroup = {
id: string;
name: string;
members: string[]; // user IDs
createdAt: Date;
updatedAt: Date;
};
// NextAuth options
export const authOptions: NextAuthOptions = {
providers: [
CredentialsProvider({
name: 'Credentials',
credentials: {
email: { label: 'Email', type: 'email' },
password: { label: 'Password', type: 'password' },
},
async authorize(credentials) {
if (!credentials?.email || !credentials?.password) {
throw new Error('Email and password required');
}
// TODO: Validate against database
const user = {
id: '1',
email: credentials.email,
name: credentials.email.split('@')[0],
role: 'user' as UserRole,
};
return user;
},
}),
GoogleProvider({
clientId: authEnv.GOOGLE_CLIENT_ID,
clientSecret: authEnv.GOOGLE_CLIENT_SECRET,
}),
AppleProvider({
clientId: authEnv.APPLE_CLIENT_ID,
clientSecret: authEnv.APPLE_CLIENT_SECRET,
}),
],
session: {
strategy: 'jwt',
maxAge: 30 * 24 * 60 * 60, // 30 days
},
pages: {
signIn: '/auth/signin',
signOut: '/auth/signout',
error: '/auth/error',
},
callbacks: {
async jwt({ token, user, account }) {
if (user) {
token.id = user.id;
token.role = (user as any).role;
}
if (account) {
token.provider = account.provider;
token.accessToken = account.access_token;
}
return token;
},
async session({ session, token }) {
if (session.user) {
session.user.id = token.id as string;
session.user.role = token.role as UserRole;
}
return session;
},
},
events: {
async createUser({ user }) {
// TODO: Create default family group
console.log('New user created:', user.email);
},
},
};