carnet api rest
This commit is contained in:
parent
ae6de407d7
commit
cc6e8a69d3
@ -35,7 +35,6 @@ declare module "next-auth" {
|
|||||||
role: string[];
|
role: string[];
|
||||||
};
|
};
|
||||||
accessToken: string;
|
accessToken: string;
|
||||||
nextcloudToken: string;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
interface JWT {
|
interface JWT {
|
||||||
@ -93,29 +92,6 @@ async function refreshAccessToken(token: JWT) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add NextCloud token exchange logic
|
|
||||||
async function exchangeKeycloakTokenForNextCloud(token: string): Promise<string> {
|
|
||||||
const response = await fetch(`${process.env.NEXTCLOUD_URL}/apps/oauth2/api/v1/token`, {
|
|
||||||
method: 'POST',
|
|
||||||
headers: {
|
|
||||||
'Content-Type': 'application/x-www-form-urlencoded',
|
|
||||||
},
|
|
||||||
body: new URLSearchParams({
|
|
||||||
grant_type: 'urn:ietf:params:oauth:grant-type:token-exchange',
|
|
||||||
subject_token: token,
|
|
||||||
subject_token_type: 'urn:ietf:params:oauth:token-type:access_token',
|
|
||||||
requested_token_type: 'urn:ietf:params:oauth:token-type:access_token'
|
|
||||||
})
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!response.ok) {
|
|
||||||
throw new Error('Failed to exchange token with NextCloud');
|
|
||||||
}
|
|
||||||
|
|
||||||
const data = await response.json();
|
|
||||||
return data.access_token;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const authOptions: NextAuthOptions = {
|
export const authOptions: NextAuthOptions = {
|
||||||
providers: [
|
providers: [
|
||||||
KeycloakProvider({
|
KeycloakProvider({
|
||||||
@ -163,31 +139,107 @@ export const authOptions: NextAuthOptions = {
|
|||||||
maxAge: 30 * 24 * 60 * 60, // 30 days
|
maxAge: 30 * 24 * 60 * 60, // 30 days
|
||||||
},
|
},
|
||||||
callbacks: {
|
callbacks: {
|
||||||
async jwt({ token, account }) {
|
async jwt({ token, account, profile }) {
|
||||||
if (account?.access_token) {
|
console.log('JWT callback start:', {
|
||||||
token.accessToken = account.access_token;
|
hasAccount: !!account,
|
||||||
// Only set refresh token if it exists
|
hasProfile: !!profile,
|
||||||
if (account.refresh_token) {
|
token
|
||||||
token.refreshToken = account.refresh_token;
|
});
|
||||||
|
|
||||||
|
if (account && profile) {
|
||||||
|
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 || [];
|
||||||
|
console.log('JWT callback raw roles:', roles);
|
||||||
|
|
||||||
|
// Clean up roles by removing ROLE_ prefix and converting to lowercase
|
||||||
|
const cleanRoles = roles.map((role: string) =>
|
||||||
|
role.replace(/^ROLE_/, '').toLowerCase()
|
||||||
|
);
|
||||||
|
|
||||||
|
console.log('JWT callback cleaned roles:', cleanRoles);
|
||||||
|
|
||||||
|
token.accessToken = account.access_token ?? '';
|
||||||
|
token.refreshToken = account.refresh_token ?? '';
|
||||||
|
token.accessTokenExpires = account.expires_at ?? 0;
|
||||||
|
token.sub = keycloakProfile.sub;
|
||||||
|
token.role = cleanRoles;
|
||||||
|
token.username = keycloakProfile.preferred_username ?? '';
|
||||||
|
token.first_name = keycloakProfile.given_name ?? '';
|
||||||
|
token.last_name = keycloakProfile.family_name ?? '';
|
||||||
|
|
||||||
|
console.log('JWT callback final token:', {
|
||||||
|
tokenRoles: token.role,
|
||||||
|
token
|
||||||
|
});
|
||||||
|
} else if (token.accessToken) {
|
||||||
|
// Decode the token to get roles
|
||||||
|
try {
|
||||||
|
const decoded = jwtDecode<DecodedToken>(token.accessToken);
|
||||||
|
console.log('Decoded token:', decoded);
|
||||||
|
|
||||||
|
if (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) =>
|
||||||
|
role.replace(/^ROLE_/, '').toLowerCase()
|
||||||
|
);
|
||||||
|
|
||||||
|
console.log('Decoded token cleaned roles:', cleanRoles);
|
||||||
|
token.role = cleanRoles;
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error decoding token:', error);
|
||||||
}
|
}
|
||||||
// Set expiry if it exists, otherwise set a default
|
|
||||||
token.accessTokenExpires = account.expires_at ? account.expires_at * 1000 : Date.now() + 3600 * 1000;
|
|
||||||
}
|
}
|
||||||
return token;
|
|
||||||
|
if (Date.now() < (token.accessTokenExpires as number) * 1000) {
|
||||||
|
return token;
|
||||||
|
}
|
||||||
|
|
||||||
|
return refreshAccessToken(token);
|
||||||
},
|
},
|
||||||
async session({ session, token }) {
|
async session({ session, token }) {
|
||||||
if (token) {
|
if (token.error) {
|
||||||
session.accessToken = token.accessToken;
|
throw new Error(token.error);
|
||||||
// We'll handle Nextcloud authentication separately using app passwords
|
|
||||||
session.user = {
|
|
||||||
...session.user,
|
|
||||||
id: token.sub || '',
|
|
||||||
role: token.role || [],
|
|
||||||
username: token.username || '',
|
|
||||||
first_name: token.first_name || '',
|
|
||||||
last_name: token.last_name || '',
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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 : [];
|
||||||
|
console.log('Session callback userRoles:', userRoles);
|
||||||
|
|
||||||
|
session.user = {
|
||||||
|
id: token.sub ?? '',
|
||||||
|
email: token.email ?? null,
|
||||||
|
name: token.name ?? null,
|
||||||
|
image: null,
|
||||||
|
username: token.username ?? '',
|
||||||
|
first_name: token.first_name ?? '',
|
||||||
|
last_name: token.last_name ?? '',
|
||||||
|
role: userRoles,
|
||||||
|
};
|
||||||
|
session.accessToken = token.accessToken;
|
||||||
|
|
||||||
|
console.log('Session callback final session:', {
|
||||||
|
userRoles: session.user.role,
|
||||||
|
session
|
||||||
|
});
|
||||||
|
|
||||||
return session;
|
return session;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@ -1,48 +0,0 @@
|
|||||||
import { NextResponse } from 'next/server';
|
|
||||||
import { getServerSession } from 'next-auth';
|
|
||||||
import { getNextCloudService } from '@/lib/nextcloud-utils';
|
|
||||||
|
|
||||||
export async function GET(
|
|
||||||
request: Request,
|
|
||||||
{ params }: { params: { date: string } }
|
|
||||||
) {
|
|
||||||
try {
|
|
||||||
const session = await getServerSession();
|
|
||||||
if (!session?.user?.email) {
|
|
||||||
return NextResponse.json(
|
|
||||||
{ error: 'Not authenticated' },
|
|
||||||
{ status: 401 }
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const { searchParams } = new URL(request.url);
|
|
||||||
const category = searchParams.get('category') || 'Notes';
|
|
||||||
|
|
||||||
const service = await getNextCloudService();
|
|
||||||
const date = new Date(params.date);
|
|
||||||
|
|
||||||
if (isNaN(date.getTime())) {
|
|
||||||
return NextResponse.json(
|
|
||||||
{ error: 'Invalid date format' },
|
|
||||||
{ status: 400 }
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const content = await service.getNote(session.user.email, category, date);
|
|
||||||
|
|
||||||
if (!content) {
|
|
||||||
return NextResponse.json(
|
|
||||||
{ error: 'Note not found' },
|
|
||||||
{ status: 404 }
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return NextResponse.json({ content, category });
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Failed to get note:', error);
|
|
||||||
return NextResponse.json(
|
|
||||||
{ error: 'Failed to get note' },
|
|
||||||
{ status: 500 }
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,59 +0,0 @@
|
|||||||
import { NextResponse } from 'next/server';
|
|
||||||
import { getServerSession } from 'next-auth';
|
|
||||||
import { getNextCloudService } from '@/lib/nextcloud-utils';
|
|
||||||
|
|
||||||
export async function GET(request: Request) {
|
|
||||||
try {
|
|
||||||
const session = await getServerSession();
|
|
||||||
if (!session?.user?.email) {
|
|
||||||
return NextResponse.json(
|
|
||||||
{ error: 'Not authenticated' },
|
|
||||||
{ status: 401 }
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const { searchParams } = new URL(request.url);
|
|
||||||
const category = searchParams.get('category');
|
|
||||||
|
|
||||||
const service = await getNextCloudService();
|
|
||||||
const notes = await service.listNotes(session.user.email, category || undefined);
|
|
||||||
|
|
||||||
return NextResponse.json({ notes });
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Failed to list notes:', error);
|
|
||||||
return NextResponse.json(
|
|
||||||
{ error: 'Failed to list notes' },
|
|
||||||
{ status: 500 }
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function POST(request: Request) {
|
|
||||||
try {
|
|
||||||
const session = await getServerSession();
|
|
||||||
if (!session?.user?.email) {
|
|
||||||
return NextResponse.json(
|
|
||||||
{ error: 'Not authenticated' },
|
|
||||||
{ status: 401 }
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const service = await getNextCloudService();
|
|
||||||
const { content, date, category = 'Notes' } = await request.json();
|
|
||||||
|
|
||||||
const result = await service.saveNote(
|
|
||||||
session.user.email,
|
|
||||||
content,
|
|
||||||
category,
|
|
||||||
date ? new Date(date) : undefined
|
|
||||||
);
|
|
||||||
|
|
||||||
return NextResponse.json(result);
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Failed to save note:', error);
|
|
||||||
return NextResponse.json(
|
|
||||||
{ error: 'Failed to save note' },
|
|
||||||
{ status: 500 }
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,58 +0,0 @@
|
|||||||
import { NextResponse } from 'next/server';
|
|
||||||
import { getServerSession } from 'next-auth';
|
|
||||||
import { authOptions } from '@/app/api/auth/[...nextauth]/route';
|
|
||||||
import { getNextCloudService } from '@/lib/nextcloud-utils';
|
|
||||||
|
|
||||||
export async function GET() {
|
|
||||||
console.log('🔍 Test endpoint called - Starting execution');
|
|
||||||
|
|
||||||
try {
|
|
||||||
// Check session
|
|
||||||
console.log('👤 Checking user session...');
|
|
||||||
const session = await getServerSession(authOptions);
|
|
||||||
|
|
||||||
if (!session?.user?.email) {
|
|
||||||
console.error('❌ No valid session found');
|
|
||||||
return NextResponse.json(
|
|
||||||
{ error: 'Unauthorized - No valid session' },
|
|
||||||
{ status: 401 }
|
|
||||||
);
|
|
||||||
}
|
|
||||||
console.log('✅ Session validated for user:', session.user.email);
|
|
||||||
|
|
||||||
// Initialize NextCloud service
|
|
||||||
console.log('🔄 Initializing NextCloud service...');
|
|
||||||
const service = await getNextCloudService();
|
|
||||||
console.log('✅ NextCloud service initialized');
|
|
||||||
|
|
||||||
// Initialize user folders
|
|
||||||
console.log('📁 Initializing user folders...');
|
|
||||||
await service.initializeUserFolders(session.user.email);
|
|
||||||
console.log('✅ User folders initialized');
|
|
||||||
|
|
||||||
// List notes
|
|
||||||
console.log('📝 Attempting to list notes...');
|
|
||||||
const notes = await service.listNotes(session.user.email);
|
|
||||||
console.log('✅ Notes retrieved successfully:', notes.length, 'notes found');
|
|
||||||
|
|
||||||
return NextResponse.json({
|
|
||||||
status: 'success',
|
|
||||||
message: 'Test completed successfully',
|
|
||||||
data: {
|
|
||||||
userEmail: session.user.email,
|
|
||||||
notesCount: notes.length,
|
|
||||||
notes: notes
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
} catch (error) {
|
|
||||||
console.error('❌ Test failed with error:', error);
|
|
||||||
return NextResponse.json(
|
|
||||||
{
|
|
||||||
error: 'Test failed',
|
|
||||||
details: error instanceof Error ? error.message : 'Unknown error'
|
|
||||||
},
|
|
||||||
{ status: 500 }
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -92,30 +92,9 @@ export default function CarnetPage() {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleNoteSave = async (note: Note) => {
|
const handleNoteSave = (note: Note) => {
|
||||||
try {
|
// TODO: Implement note saving logic
|
||||||
const response = await fetch('/api/carnet', {
|
console.log('Saving note:', note);
|
||||||
method: 'POST',
|
|
||||||
headers: {
|
|
||||||
'Content-Type': 'application/json',
|
|
||||||
},
|
|
||||||
body: JSON.stringify({
|
|
||||||
title: note.title,
|
|
||||||
content: note.content,
|
|
||||||
date: note.lastEdited.toISOString(),
|
|
||||||
category: 'Notes'
|
|
||||||
}),
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!response.ok) {
|
|
||||||
throw new Error('Failed to save note');
|
|
||||||
}
|
|
||||||
|
|
||||||
const result = await response.json();
|
|
||||||
console.log('Note saved successfully:', result);
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Error saving note:', error);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
if (isLoading) {
|
if (isLoading) {
|
||||||
|
|||||||
@ -8,9 +8,8 @@ interface EditorProps {
|
|||||||
id: string;
|
id: string;
|
||||||
title: string;
|
title: string;
|
||||||
content: string;
|
content: string;
|
||||||
lastEdited: Date;
|
};
|
||||||
} | null;
|
onSave?: (note: { id: string; title: string; content: string }) => void;
|
||||||
onSave?: (note: { id: string; title: string; content: string; lastEdited: Date }) => void;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const Editor: React.FC<EditorProps> = ({ note, onSave }) => {
|
export const Editor: React.FC<EditorProps> = ({ note, onSave }) => {
|
||||||
@ -33,12 +32,11 @@ export const Editor: React.FC<EditorProps> = ({ note, onSave }) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const handleSave = () => {
|
const handleSave = () => {
|
||||||
if (onSave) {
|
if (note?.id) {
|
||||||
onSave({
|
onSave?.({
|
||||||
id: note?.id || Date.now().toString(),
|
id: note.id,
|
||||||
title,
|
title,
|
||||||
content,
|
content
|
||||||
lastEdited: new Date()
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@ -1,37 +0,0 @@
|
|||||||
import { getServerSession } from 'next-auth';
|
|
||||||
import { NextCloudService } from './nextcloud';
|
|
||||||
|
|
||||||
export async function getNextCloudService() {
|
|
||||||
const session = await getServerSession();
|
|
||||||
if (!session?.user?.email) {
|
|
||||||
throw new Error('Not authenticated');
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get the NextCloud token from the session
|
|
||||||
const token = session.nextcloudToken;
|
|
||||||
if (!token) {
|
|
||||||
console.error('Session details:', {
|
|
||||||
hasSession: !!session,
|
|
||||||
hasUser: !!session?.user,
|
|
||||||
email: session?.user?.email,
|
|
||||||
sessionKeys: Object.keys(session || {}),
|
|
||||||
// Don't log sensitive data
|
|
||||||
tokenPresent: !!token,
|
|
||||||
hasNextCloudToken: 'nextcloudToken' in session
|
|
||||||
});
|
|
||||||
throw new Error('No NextCloud token available');
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
console.log('Initializing NextCloud service with token length:', token.length);
|
|
||||||
const service = new NextCloudService(token);
|
|
||||||
return service;
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Failed to initialize NextCloud service:', {
|
|
||||||
error: error instanceof Error ? error.message : error,
|
|
||||||
hasToken: !!token,
|
|
||||||
tokenLength: token.length
|
|
||||||
});
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
305
lib/nextcloud.ts
305
lib/nextcloud.ts
@ -1,305 +0,0 @@
|
|||||||
import { WebDAV } from 'webdav';
|
|
||||||
import { getSession } from 'next-auth/react';
|
|
||||||
|
|
||||||
export class NextCloudService {
|
|
||||||
private webdav: WebDAV;
|
|
||||||
private basePath: string = '/Personal/Carnet';
|
|
||||||
private subFolders: string[] = ['Journal', 'Santé', 'Notes'];
|
|
||||||
private token: string;
|
|
||||||
private appPassword?: string;
|
|
||||||
private baseUrl: string;
|
|
||||||
|
|
||||||
constructor(token: string, appPassword?: string) {
|
|
||||||
this.token = token;
|
|
||||||
this.appPassword = appPassword;
|
|
||||||
this.baseUrl = process.env.NEXTCLOUD_URL || '';
|
|
||||||
console.log('NextCloud Configuration:', {
|
|
||||||
baseUrl: this.baseUrl,
|
|
||||||
tokenLength: token?.length || 0,
|
|
||||||
hasToken: !!token
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!this.baseUrl) {
|
|
||||||
throw new Error('NEXTCLOUD_URL environment variable is not set');
|
|
||||||
}
|
|
||||||
|
|
||||||
const webdavUrl = `${this.baseUrl}/remote.php/dav/files`;
|
|
||||||
console.log('WebDAV endpoint:', webdavUrl);
|
|
||||||
|
|
||||||
this.webdav = new WebDAV(
|
|
||||||
webdavUrl,
|
|
||||||
{
|
|
||||||
headers: this.getHeaders(),
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
private isUsingAppPassword(): boolean {
|
|
||||||
return !!this.appPassword;
|
|
||||||
}
|
|
||||||
|
|
||||||
private getHeaders(): HeadersInit {
|
|
||||||
if (this.isUsingAppPassword()) {
|
|
||||||
return {
|
|
||||||
'Authorization': `Basic ${Buffer.from(`${this.token}:${this.appPassword}`).toString('base64')}`,
|
|
||||||
'Content-Type': 'application/json',
|
|
||||||
};
|
|
||||||
}
|
|
||||||
return {
|
|
||||||
'Authorization': `Bearer ${this.token}`,
|
|
||||||
'Content-Type': 'application/json',
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
private async testConnection() {
|
|
||||||
try {
|
|
||||||
console.log('Testing WebDAV connection...');
|
|
||||||
// Try to list root directory
|
|
||||||
const rootContents = await this.webdav.getDirectoryContents('/');
|
|
||||||
console.log('WebDAV connection successful, root directory contents:', rootContents);
|
|
||||||
return true;
|
|
||||||
} catch (error) {
|
|
||||||
console.error('WebDAV connection test failed:', {
|
|
||||||
error: error instanceof Error ? error.message : error,
|
|
||||||
response: error.response?.data,
|
|
||||||
status: error.response?.status,
|
|
||||||
headers: error.response?.headers
|
|
||||||
});
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async initializeUserFolders(username: string) {
|
|
||||||
console.log('=== Starting folder initialization ===');
|
|
||||||
console.log(`User: ${username}`);
|
|
||||||
|
|
||||||
// Test connection first
|
|
||||||
const connectionOk = await this.testConnection();
|
|
||||||
if (!connectionOk) {
|
|
||||||
throw new Error('Failed to connect to NextCloud WebDAV service');
|
|
||||||
}
|
|
||||||
|
|
||||||
const userBasePath = `${username}${this.basePath}`;
|
|
||||||
console.log('Target base path:', userBasePath);
|
|
||||||
|
|
||||||
// Create base Carnet folder
|
|
||||||
try {
|
|
||||||
console.log('Checking base folder existence...');
|
|
||||||
const baseExists = await this.webdav.exists(userBasePath);
|
|
||||||
console.log('Base folder check result:', baseExists);
|
|
||||||
|
|
||||||
if (!baseExists) {
|
|
||||||
console.log('Creating base folder structure...');
|
|
||||||
try {
|
|
||||||
await this.webdav.createDirectory(userBasePath, { recursive: true });
|
|
||||||
console.log('Base folder created successfully');
|
|
||||||
} catch (createError) {
|
|
||||||
console.error('Failed to create base folder:', {
|
|
||||||
error: createError instanceof Error ? createError.message : createError,
|
|
||||||
response: createError.response?.data,
|
|
||||||
status: createError.response?.status
|
|
||||||
});
|
|
||||||
throw createError;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Error in base folder operation:', {
|
|
||||||
path: userBasePath,
|
|
||||||
error: error instanceof Error ? error.message : error,
|
|
||||||
response: error.response?.data,
|
|
||||||
status: error.response?.status,
|
|
||||||
headers: error.response?.headers
|
|
||||||
});
|
|
||||||
throw new Error(`Failed to initialize base folder: ${error.message || 'Unknown error'}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create subfolders
|
|
||||||
for (const folder of this.subFolders) {
|
|
||||||
const folderPath = `${userBasePath}/${folder}`;
|
|
||||||
console.log(`\nProcessing subfolder: ${folder}`);
|
|
||||||
console.log('Target path:', folderPath);
|
|
||||||
|
|
||||||
try {
|
|
||||||
console.log(`Checking if ${folder} exists...`);
|
|
||||||
const exists = await this.webdav.exists(folderPath);
|
|
||||||
console.log(`${folder} existence check result:`, exists);
|
|
||||||
|
|
||||||
if (!exists) {
|
|
||||||
console.log(`Creating ${folder} folder...`);
|
|
||||||
try {
|
|
||||||
await this.webdav.createDirectory(folderPath, { recursive: true });
|
|
||||||
console.log(`${folder} folder created successfully`);
|
|
||||||
} catch (createError) {
|
|
||||||
console.error(`Failed to create ${folder} folder:`, {
|
|
||||||
error: createError instanceof Error ? createError.message : createError,
|
|
||||||
response: createError.response?.data,
|
|
||||||
status: createError.response?.status
|
|
||||||
});
|
|
||||||
throw createError;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.error(`Error processing ${folder} folder:`, {
|
|
||||||
path: folderPath,
|
|
||||||
error: error instanceof Error ? error.message : error,
|
|
||||||
response: error.response?.data,
|
|
||||||
status: error.response?.status,
|
|
||||||
headers: error.response?.headers
|
|
||||||
});
|
|
||||||
throw new Error(`Failed to initialize ${folder} folder: ${error.message || 'Unknown error'}`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log('\n=== Folder initialization completed ===');
|
|
||||||
|
|
||||||
// Verify final structure
|
|
||||||
try {
|
|
||||||
console.log('\nVerifying folder structure...');
|
|
||||||
const contents = await this.webdav.getDirectoryContents(userBasePath, { deep: true });
|
|
||||||
console.log('Final folder structure:', contents);
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Error verifying folder structure:', {
|
|
||||||
error: error instanceof Error ? error.message : error,
|
|
||||||
response: error.response?.data,
|
|
||||||
status: error.response?.status
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async saveNote(username: string, content: string, category: string = 'Notes', date: Date = new Date()) {
|
|
||||||
console.log('Saving note:', { username, category, date });
|
|
||||||
|
|
||||||
if (!this.subFolders.includes(category)) {
|
|
||||||
console.error('Invalid category provided:', category);
|
|
||||||
throw new Error(`Invalid category: ${category}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
const fileName = `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, '0')}-${String(date.getDate()).padStart(2, '0')}.md`;
|
|
||||||
const filePath = `${username}${this.basePath}/${category}/${fileName}`;
|
|
||||||
console.log('File path for save:', filePath);
|
|
||||||
|
|
||||||
try {
|
|
||||||
await this.initializeUserFolders(username);
|
|
||||||
console.log('Saving file content to:', filePath);
|
|
||||||
await this.webdav.putFileContents(filePath, content, { overwrite: true });
|
|
||||||
console.log('File saved successfully');
|
|
||||||
|
|
||||||
return { fileName, category };
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Error saving note:', {
|
|
||||||
path: filePath,
|
|
||||||
error: error instanceof Error ? error.message : error,
|
|
||||||
fullError: error
|
|
||||||
});
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async getNote(username: string, category: string, date: Date) {
|
|
||||||
console.log('Getting note:', { username, category, date });
|
|
||||||
|
|
||||||
if (!this.subFolders.includes(category)) {
|
|
||||||
console.error('Invalid category provided:', category);
|
|
||||||
throw new Error(`Invalid category: ${category}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
const fileName = `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, '0')}-${String(date.getDate()).padStart(2, '0')}.md`;
|
|
||||||
const filePath = `${username}${this.basePath}/${category}/${fileName}`;
|
|
||||||
console.log('File path for get:', filePath);
|
|
||||||
|
|
||||||
try {
|
|
||||||
console.log('Fetching file content from:', filePath);
|
|
||||||
const content = await this.webdav.getFileContents(filePath, { format: 'text' });
|
|
||||||
console.log('File content fetched successfully');
|
|
||||||
return content;
|
|
||||||
} catch (error) {
|
|
||||||
if (error.response?.status === 404) {
|
|
||||||
console.log('Note not found:', filePath);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
console.error('Error getting note:', {
|
|
||||||
path: filePath,
|
|
||||||
error: error instanceof Error ? error.message : error,
|
|
||||||
fullError: error
|
|
||||||
});
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async listNotes(username: string, category?: string) {
|
|
||||||
console.log('Listing notes:', { username, category });
|
|
||||||
|
|
||||||
try {
|
|
||||||
await this.initializeUserFolders(username);
|
|
||||||
|
|
||||||
const userPath = `${username}${this.basePath}`;
|
|
||||||
const results = [];
|
|
||||||
|
|
||||||
if (category) {
|
|
||||||
if (!this.subFolders.includes(category)) {
|
|
||||||
console.error('Invalid category provided:', category);
|
|
||||||
throw new Error(`Invalid category: ${category}`);
|
|
||||||
}
|
|
||||||
const folderPath = `${userPath}/${category}`;
|
|
||||||
console.log('Listing files in category folder:', folderPath);
|
|
||||||
const files = await this.webdav.getDirectoryContents(folderPath);
|
|
||||||
console.log(`Found ${files.length} files in ${category}`);
|
|
||||||
results.push(...this.processFiles(files, category));
|
|
||||||
} else {
|
|
||||||
for (const folder of this.subFolders) {
|
|
||||||
const folderPath = `${userPath}/${folder}`;
|
|
||||||
try {
|
|
||||||
console.log('Listing files in folder:', folderPath);
|
|
||||||
const files = await this.webdav.getDirectoryContents(folderPath);
|
|
||||||
console.log(`Found ${files.length} files in ${folder}`);
|
|
||||||
results.push(...this.processFiles(files, folder));
|
|
||||||
} catch (error) {
|
|
||||||
if (error.response?.status !== 404) {
|
|
||||||
console.error(`Error listing files in ${folder}:`, {
|
|
||||||
path: folderPath,
|
|
||||||
error: error instanceof Error ? error.message : error,
|
|
||||||
fullError: error
|
|
||||||
});
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
console.log(`No files found in ${folder}`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log(`Total notes found: ${results.length}`);
|
|
||||||
return results;
|
|
||||||
} catch (error) {
|
|
||||||
if (error.response?.status === 404) {
|
|
||||||
console.log('No notes found');
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
console.error('Error listing notes:', {
|
|
||||||
username,
|
|
||||||
category,
|
|
||||||
error: error instanceof Error ? error.message : error,
|
|
||||||
fullError: error
|
|
||||||
});
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private processFiles(files: any[], category: string) {
|
|
||||||
const processed = files
|
|
||||||
.filter(file => file.basename.endsWith('.md'))
|
|
||||||
.map(file => ({
|
|
||||||
date: this.fileNameToDate(file.basename),
|
|
||||||
name: file.basename,
|
|
||||||
path: file.filename,
|
|
||||||
category
|
|
||||||
}));
|
|
||||||
console.log(`Processed ${processed.length} files in ${category}`);
|
|
||||||
return processed;
|
|
||||||
}
|
|
||||||
|
|
||||||
private fileNameToDate(fileName: string): Date {
|
|
||||||
const [year, month, day] = fileName.split('.')[0].split('-').map(Number);
|
|
||||||
return new Date(year, month - 1, day);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
38
types/next-auth.d.ts
vendored
38
types/next-auth.d.ts
vendored
@ -7,23 +7,26 @@ declare module "next-auth" {
|
|||||||
first_name: string;
|
first_name: string;
|
||||||
last_name: string;
|
last_name: string;
|
||||||
username: string;
|
username: string;
|
||||||
email?: string | null;
|
email: string;
|
||||||
role: string[];
|
role: string[];
|
||||||
} & DefaultSession["user"];
|
} & DefaultSession["user"];
|
||||||
accessToken: string;
|
accessToken?: string;
|
||||||
refreshToken?: string;
|
refreshToken?: string;
|
||||||
|
rocketChatToken?: string | null;
|
||||||
|
rocketChatUserId?: string | null;
|
||||||
error?: string;
|
error?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface JWT {
|
interface JWT {
|
||||||
sub?: string;
|
accessToken?: string;
|
||||||
accessToken: string;
|
|
||||||
refreshToken?: string;
|
refreshToken?: string;
|
||||||
accessTokenExpires: number;
|
accessTokenExpires?: number;
|
||||||
first_name: string;
|
first_name?: string;
|
||||||
last_name: string;
|
last_name?: string;
|
||||||
username: string;
|
username?: string;
|
||||||
role: string[];
|
role?: string[];
|
||||||
|
rocketChatToken?: string | null;
|
||||||
|
rocketChatUserId?: string | null;
|
||||||
error?: string;
|
error?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -32,7 +35,7 @@ declare module "next-auth" {
|
|||||||
first_name: string;
|
first_name: string;
|
||||||
last_name: string;
|
last_name: string;
|
||||||
username: string;
|
username: string;
|
||||||
email?: string | null;
|
email: string;
|
||||||
role: string[];
|
role: string[];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -50,14 +53,15 @@ declare module "next-auth" {
|
|||||||
|
|
||||||
declare module "next-auth/jwt" {
|
declare module "next-auth/jwt" {
|
||||||
interface JWT {
|
interface JWT {
|
||||||
sub?: string;
|
accessToken?: string;
|
||||||
accessToken: string;
|
|
||||||
refreshToken?: string;
|
refreshToken?: string;
|
||||||
accessTokenExpires: number;
|
accessTokenExpires?: number;
|
||||||
first_name: string;
|
first_name?: string;
|
||||||
last_name: string;
|
last_name?: string;
|
||||||
username: string;
|
username?: string;
|
||||||
role: string[];
|
role?: string[];
|
||||||
|
rocketChatToken?: string;
|
||||||
|
rocketChatUserId?: string;
|
||||||
error?: string;
|
error?: string;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user