Page sec
This commit is contained in:
parent
8783f195fa
commit
8ab7ddc7b0
@ -39,14 +39,16 @@ declare module "next-auth" {
|
|||||||
|
|
||||||
interface JWT {
|
interface JWT {
|
||||||
sub?: string;
|
sub?: string;
|
||||||
accessToken: string;
|
accessToken?: string;
|
||||||
refreshToken: string;
|
refreshToken?: string;
|
||||||
accessTokenExpires: number;
|
accessTokenExpires?: number;
|
||||||
role: string[];
|
role?: string[];
|
||||||
username: string;
|
username?: string;
|
||||||
first_name: string;
|
first_name?: string;
|
||||||
last_name: string;
|
last_name?: string;
|
||||||
error?: string;
|
error?: string;
|
||||||
|
email?: string | null;
|
||||||
|
name?: string | null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -140,31 +142,13 @@ export const authOptions: NextAuthOptions = {
|
|||||||
},
|
},
|
||||||
callbacks: {
|
callbacks: {
|
||||||
async jwt({ token, account, profile }) {
|
async jwt({ token, account, profile }) {
|
||||||
console.log('JWT callback start:', {
|
|
||||||
hasAccount: !!account,
|
|
||||||
hasProfile: !!profile,
|
|
||||||
token
|
|
||||||
});
|
|
||||||
|
|
||||||
if (account && profile) {
|
if (account && profile) {
|
||||||
const keycloakProfile = profile as KeycloakProfile;
|
const keycloakProfile = profile as KeycloakProfile;
|
||||||
console.log('JWT callback profile:', {
|
|
||||||
rawRoles: keycloakProfile.roles,
|
|
||||||
realmAccess: keycloakProfile.realm_access,
|
|
||||||
profile: keycloakProfile
|
|
||||||
});
|
|
||||||
|
|
||||||
// Get roles from realm_access
|
|
||||||
const roles = keycloakProfile.realm_access?.roles || [];
|
const roles = keycloakProfile.realm_access?.roles || [];
|
||||||
console.log('JWT callback raw roles:', roles);
|
|
||||||
|
|
||||||
// Clean up roles by removing ROLE_ prefix and converting to lowercase
|
|
||||||
const cleanRoles = roles.map((role: string) =>
|
const cleanRoles = roles.map((role: string) =>
|
||||||
role.replace(/^ROLE_/, '').toLowerCase()
|
role.replace(/^ROLE_/, '').toLowerCase()
|
||||||
);
|
);
|
||||||
|
|
||||||
console.log('JWT callback cleaned roles:', cleanRoles);
|
|
||||||
|
|
||||||
token.accessToken = account.access_token ?? '';
|
token.accessToken = account.access_token ?? '';
|
||||||
token.refreshToken = account.refresh_token ?? '';
|
token.refreshToken = account.refresh_token ?? '';
|
||||||
token.accessTokenExpires = account.expires_at ?? 0;
|
token.accessTokenExpires = account.expires_at ?? 0;
|
||||||
@ -173,27 +157,14 @@ export const authOptions: NextAuthOptions = {
|
|||||||
token.username = keycloakProfile.preferred_username ?? '';
|
token.username = keycloakProfile.preferred_username ?? '';
|
||||||
token.first_name = keycloakProfile.given_name ?? '';
|
token.first_name = keycloakProfile.given_name ?? '';
|
||||||
token.last_name = keycloakProfile.family_name ?? '';
|
token.last_name = keycloakProfile.family_name ?? '';
|
||||||
|
|
||||||
console.log('JWT callback final token:', {
|
|
||||||
tokenRoles: token.role,
|
|
||||||
token
|
|
||||||
});
|
|
||||||
} else if (token.accessToken) {
|
} else if (token.accessToken) {
|
||||||
// Decode the token to get roles
|
|
||||||
try {
|
try {
|
||||||
const decoded = jwtDecode<DecodedToken>(token.accessToken);
|
const decoded = jwtDecode<DecodedToken>(token.accessToken);
|
||||||
console.log('Decoded token:', decoded);
|
|
||||||
|
|
||||||
if (decoded.realm_access?.roles) {
|
if (decoded.realm_access?.roles) {
|
||||||
const roles = decoded.realm_access.roles;
|
const roles = decoded.realm_access.roles;
|
||||||
console.log('Decoded token roles:', roles);
|
|
||||||
|
|
||||||
// Clean up roles by removing ROLE_ prefix and converting to lowercase
|
|
||||||
const cleanRoles = roles.map((role: string) =>
|
const cleanRoles = roles.map((role: string) =>
|
||||||
role.replace(/^ROLE_/, '').toLowerCase()
|
role.replace(/^ROLE_/, '').toLowerCase()
|
||||||
);
|
);
|
||||||
|
|
||||||
console.log('Decoded token cleaned roles:', cleanRoles);
|
|
||||||
token.role = cleanRoles;
|
token.role = cleanRoles;
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@ -212,17 +183,7 @@ export const authOptions: NextAuthOptions = {
|
|||||||
throw new Error(token.error);
|
throw new Error(token.error);
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('Session callback token:', {
|
|
||||||
tokenRoles: token.role,
|
|
||||||
tokenSub: token.sub,
|
|
||||||
tokenUsername: token.username,
|
|
||||||
token
|
|
||||||
});
|
|
||||||
|
|
||||||
// Ensure we have an array of roles
|
|
||||||
const userRoles = Array.isArray(token.role) ? token.role : [];
|
const userRoles = Array.isArray(token.role) ? token.role : [];
|
||||||
console.log('Session callback userRoles:', userRoles);
|
|
||||||
|
|
||||||
session.user = {
|
session.user = {
|
||||||
id: token.sub ?? '',
|
id: token.sub ?? '',
|
||||||
email: token.email ?? null,
|
email: token.email ?? null,
|
||||||
@ -235,11 +196,6 @@ export const authOptions: NextAuthOptions = {
|
|||||||
};
|
};
|
||||||
session.accessToken = token.accessToken;
|
session.accessToken = token.accessToken;
|
||||||
|
|
||||||
console.log('Session callback final session:', {
|
|
||||||
userRoles: session.user.role,
|
|
||||||
session
|
|
||||||
});
|
|
||||||
|
|
||||||
return session;
|
return session;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@ -31,6 +31,20 @@ interface ContactFile {
|
|||||||
lastmod: string;
|
lastmod: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface Contact {
|
||||||
|
id: string;
|
||||||
|
name: string;
|
||||||
|
email: string;
|
||||||
|
phone: string;
|
||||||
|
address: string;
|
||||||
|
notes: string;
|
||||||
|
tags: string[];
|
||||||
|
created_at: string;
|
||||||
|
updated_at: string;
|
||||||
|
user_id: string;
|
||||||
|
vcard: string;
|
||||||
|
}
|
||||||
|
|
||||||
export default function Navigation({ nextcloudFolders, onFolderSelect }: NavigationProps) {
|
export default function Navigation({ nextcloudFolders, onFolderSelect }: NavigationProps) {
|
||||||
const [searchQuery, setSearchQuery] = useState('');
|
const [searchQuery, setSearchQuery] = useState('');
|
||||||
const [expandedContacts, setExpandedContacts] = useState(false);
|
const [expandedContacts, setExpandedContacts] = useState(false);
|
||||||
@ -65,7 +79,8 @@ export default function Navigation({ nextcloudFolders, onFolderSelect }: Navigat
|
|||||||
const response = await fetch('/api/nextcloud/files?folder=Contacts');
|
const response = await fetch('/api/nextcloud/files?folder=Contacts');
|
||||||
if (response.ok) {
|
if (response.ok) {
|
||||||
const files = await response.json();
|
const files = await response.json();
|
||||||
console.log('Received files:', files); // Debug log
|
// Only log the number of files received, not their contents
|
||||||
|
console.log(`Received ${files.length} files from Nextcloud`);
|
||||||
// Filter for VCF files and map to ContactFile interface
|
// Filter for VCF files and map to ContactFile interface
|
||||||
const vcfFiles = files
|
const vcfFiles = files
|
||||||
.filter((file: any) => file.basename.endsWith('.vcf'))
|
.filter((file: any) => file.basename.endsWith('.vcf'))
|
||||||
@ -75,7 +90,8 @@ export default function Navigation({ nextcloudFolders, onFolderSelect }: Navigat
|
|||||||
basename: file.basename,
|
basename: file.basename,
|
||||||
lastmod: file.lastmod
|
lastmod: file.lastmod
|
||||||
}));
|
}));
|
||||||
console.log('Processed VCF files:', vcfFiles); // Debug log
|
// Only log the number of VCF files processed
|
||||||
|
console.log(`Processed ${vcfFiles.length} VCF files`);
|
||||||
setContactFiles(vcfFiles);
|
setContactFiles(vcfFiles);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@ -92,10 +108,58 @@ export default function Navigation({ nextcloudFolders, onFolderSelect }: Navigat
|
|||||||
}
|
}
|
||||||
}, [expandedContacts]);
|
}, [expandedContacts]);
|
||||||
|
|
||||||
// Debug log for contactFiles state
|
const handleVcfFile = async (file: File) => {
|
||||||
useEffect(() => {
|
try {
|
||||||
console.log('Current contactFiles state:', contactFiles);
|
const text = await file.text();
|
||||||
}, [contactFiles]);
|
const vcards = text.split('END:VCARD').filter(vcard => vcard.trim());
|
||||||
|
|
||||||
|
const processedContacts = vcards.map(vcard => {
|
||||||
|
const lines = vcard.split('\n');
|
||||||
|
const contact: Contact = {
|
||||||
|
id: crypto.randomUUID(),
|
||||||
|
name: '',
|
||||||
|
email: '',
|
||||||
|
phone: '',
|
||||||
|
address: '',
|
||||||
|
notes: '',
|
||||||
|
tags: [],
|
||||||
|
created_at: new Date().toISOString(),
|
||||||
|
updated_at: new Date().toISOString(),
|
||||||
|
user_id: '',
|
||||||
|
vcard: vcard + 'END:VCARD'
|
||||||
|
};
|
||||||
|
|
||||||
|
lines.forEach(line => {
|
||||||
|
if (line.startsWith('FN:')) {
|
||||||
|
contact.name = line.substring(3);
|
||||||
|
} else if (line.startsWith('EMAIL;')) {
|
||||||
|
contact.email = line.split(':')[1];
|
||||||
|
} else if (line.startsWith('TEL;')) {
|
||||||
|
contact.phone = line.split(':')[1];
|
||||||
|
} else if (line.startsWith('ADR;')) {
|
||||||
|
contact.address = line.split(':')[1];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return contact;
|
||||||
|
});
|
||||||
|
|
||||||
|
// Only log the number of contacts processed, not their details
|
||||||
|
console.log(`Processed ${processedContacts.length} contacts from VCF file`);
|
||||||
|
|
||||||
|
// Convert Contact objects to ContactFile objects
|
||||||
|
const contactFiles: ContactFile[] = processedContacts.map(contact => ({
|
||||||
|
id: contact.id,
|
||||||
|
filename: `${contact.name}.vcf`,
|
||||||
|
basename: contact.name,
|
||||||
|
lastmod: new Date().toISOString()
|
||||||
|
}));
|
||||||
|
|
||||||
|
setContactFiles(prev => [...prev, ...contactFiles]);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error processing VCF file:', error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-col h-full bg-carnet-sidebar">
|
<div className="flex flex-col h-full bg-carnet-sidebar">
|
||||||
@ -164,7 +228,6 @@ export default function Navigation({ nextcloudFolders, onFolderSelect }: Navigat
|
|||||||
<div className="px-3 py-2 text-sm text-carnet-text-muted">Aucun contact</div>
|
<div className="px-3 py-2 text-sm text-carnet-text-muted">Aucun contact</div>
|
||||||
) : (
|
) : (
|
||||||
contactFiles.map((file) => {
|
contactFiles.map((file) => {
|
||||||
console.log('Rendering contact file:', file); // Debug log
|
|
||||||
return (
|
return (
|
||||||
<button
|
<button
|
||||||
key={file.id}
|
key={file.id}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user