Skip to content

Commit 0f94732

Browse files
bug fix: removed storage.ts file from .gitignore
/lib/storage.ts file was causing errors in a lot of user's system because it was stored in git ignore folder. Removed it and made small bug fixes on json and markdown files download and everything works on clean install.
1 parent b2d7985 commit 0f94732

File tree

5 files changed

+191
-102
lines changed

5 files changed

+191
-102
lines changed

.gitignore

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,6 @@ var/
5050
sdist/
5151
develop-eggs/
5252
.installed.cfg
53-
lib/
5453
lib64/
5554
*.egg
5655

app/api/storage/download/route.ts

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
import { NextResponse } from 'next/server'
2+
import fs from 'fs/promises'
3+
import path from 'path'
4+
5+
export async function GET(request: Request) {
6+
try {
7+
const { searchParams } = new URL(request.url)
8+
const filePath = searchParams.get('path')
9+
10+
if (!filePath) {
11+
return NextResponse.json(
12+
{ success: false, error: 'No file path provided' },
13+
{ status: 400 }
14+
)
15+
}
16+
17+
// Security check to ensure the path is within the storage directory
18+
const storagePath = path.join(process.cwd(), 'storage/markdown')
19+
const normalizedPath = path.normalize(filePath)
20+
if (!normalizedPath.startsWith(storagePath)) {
21+
return NextResponse.json(
22+
{ success: false, error: 'Invalid file path' },
23+
{ status: 403 }
24+
)
25+
}
26+
27+
// Check if file exists
28+
try {
29+
await fs.access(normalizedPath)
30+
} catch {
31+
return NextResponse.json(
32+
{ success: false, error: 'File not found' },
33+
{ status: 404 }
34+
)
35+
}
36+
37+
// Read the file
38+
const content = await fs.readFile(normalizedPath, 'utf-8')
39+
40+
// If it's a JSON file, verify it's valid JSON
41+
if (path.extname(filePath) === '.json') {
42+
try {
43+
JSON.parse(content)
44+
} catch {
45+
return NextResponse.json(
46+
{ success: false, error: 'Invalid JSON file' },
47+
{ status: 500 }
48+
)
49+
}
50+
}
51+
52+
// Determine content type based on file extension
53+
const contentType = path.extname(filePath) === '.json'
54+
? 'application/json'
55+
: 'text/markdown'
56+
57+
// Create response with appropriate headers for download
58+
return new NextResponse(content, {
59+
headers: {
60+
'Content-Type': contentType,
61+
'Content-Disposition': `attachment; filename="${path.basename(filePath)}"`,
62+
},
63+
})
64+
} catch (error) {
65+
console.error('Error downloading file:', error)
66+
return NextResponse.json(
67+
{
68+
success: false,
69+
error: error instanceof Error ? error.message : 'Failed to download file'
70+
},
71+
{ status: 500 }
72+
)
73+
}
74+
}

app/api/storage/route.ts

Lines changed: 70 additions & 98 deletions
Original file line numberDiff line numberDiff line change
@@ -1,124 +1,96 @@
1-
import { NextRequest, NextResponse } from 'next/server'
2-
import fs from 'fs'
1+
import { NextResponse } from 'next/server'
2+
import fs from 'fs/promises'
33
import path from 'path'
4-
import { URL } from 'url'
54

6-
const STORAGE_DIR = path.join(process.cwd(), 'storage', 'markdown')
5+
const STORAGE_DIR = path.join(process.cwd(), 'storage/markdown')
76

8-
// Ensure storage directory exists
9-
if (!fs.existsSync(STORAGE_DIR)) {
10-
fs.mkdirSync(STORAGE_DIR, { recursive: true })
11-
}
12-
13-
function getFilenameFromUrl(url: string): string {
14-
try {
15-
const parsedUrl = new URL(url)
16-
// Use hostname and pathname to create a unique filename
17-
const filename = `${parsedUrl.hostname}${parsedUrl.pathname.replace(/\//g, '_')}`
18-
.replace(/[^a-zA-Z0-9-_]/g, '_') // Replace invalid chars with underscore
19-
.replace(/_+/g, '_') // Replace multiple underscores with single
20-
.toLowerCase()
21-
return `${filename}.json`
22-
} catch (error) {
23-
// Fallback for invalid URLs
24-
return `${url.replace(/[^a-zA-Z0-9-_]/g, '_')}.json`
25-
}
26-
}
27-
28-
// POST /api/storage - Save markdown
29-
export async function POST(request: NextRequest) {
7+
export async function POST(request: Request) {
308
try {
31-
const { url, content, stats } = await request.json()
32-
const filename = getFilenameFromUrl(url)
33-
const filepath = path.join(STORAGE_DIR, filename)
34-
35-
const data = {
36-
url,
37-
content,
38-
timestamp: new Date().toISOString(),
39-
stats
40-
}
41-
42-
// Save JSON file
43-
fs.writeFileSync(filepath, JSON.stringify(data, null, 2))
9+
const { url, content } = await request.json()
10+
11+
// Create storage directory if it doesn't exist
12+
await fs.mkdir(STORAGE_DIR, { recursive: true })
13+
14+
// Generate filename from URL
15+
const filename = url
16+
.replace(/^https?:\/\//, '')
17+
.replace(/[^a-z0-9]/gi, '_')
18+
.toLowerCase() + '.md'
19+
20+
const filePath = path.join(STORAGE_DIR, filename)
21+
await fs.writeFile(filePath, content, 'utf-8')
4422

45-
// Save markdown file
46-
const markdownPath = filepath.replace('.json', '.md')
47-
fs.writeFileSync(markdownPath, content)
48-
4923
return NextResponse.json({ success: true })
5024
} catch (error) {
51-
console.error('Error saving markdown:', error)
5225
return NextResponse.json(
53-
{ error: 'Failed to save markdown' },
26+
{ success: false, error: error instanceof Error ? error.message : 'Failed to save markdown' },
5427
{ status: 500 }
5528
)
5629
}
5730
}
5831

59-
// GET /api/storage - List files
60-
// GET /api/storage?url=... - Load specific file
61-
export async function GET(request: NextRequest) {
32+
export async function GET(request: Request) {
6233
try {
63-
const url = request.nextUrl.searchParams.get('url')
34+
const { searchParams } = new URL(request.url)
35+
const url = searchParams.get('url')
6436

65-
// If no URL provided, list all files
37+
// Handle list request
6638
if (!url) {
67-
const files = fs.readdirSync(STORAGE_DIR)
68-
.filter(file => file.endsWith('.json'))
69-
.map(file => {
70-
const name = file.replace('.json', '')
71-
const jsonPath = `storage/markdown/${file}`
72-
const markdownPath = `storage/markdown/${name}.md`
39+
// Only get .md files
40+
const files = await fs.readdir(STORAGE_DIR)
41+
const mdFiles = files.filter(f => f.endsWith('.md'))
42+
43+
const fileDetails = await Promise.all(
44+
mdFiles.map(async (filename) => {
45+
const mdPath = path.join(STORAGE_DIR, filename)
46+
const jsonPath = path.join(STORAGE_DIR, filename.replace('.md', '.json'))
47+
const stats = await fs.stat(mdPath)
48+
const content = await fs.readFile(mdPath, 'utf-8')
7349

74-
// Only include if both files exist
75-
if (fs.existsSync(path.join(process.cwd(), jsonPath)) &&
76-
fs.existsSync(path.join(process.cwd(), markdownPath))) {
77-
// Get file stats
78-
const stats = fs.statSync(path.join(process.cwd(), jsonPath))
79-
const content = JSON.parse(fs.readFileSync(path.join(process.cwd(), jsonPath), 'utf-8'))
80-
81-
// Clean up the name by removing common prefixes and file extensions
82-
const cleanName = name
83-
.replace(/^docs[._]/, '') // Remove leading docs prefix
84-
.replace(/\.json$/, '') // Remove .json extension
85-
.replace(/\.md$/, '') // Remove .md extension
86-
87-
return {
88-
name: cleanName, // Use cleaned name
89-
jsonPath,
90-
markdownPath,
91-
timestamp: stats.mtime, // Keep as Date for sorting
92-
size: stats.size,
93-
wordCount: content.stats?.wordCount || 0,
94-
charCount: content.stats?.charCount || 0
95-
}
50+
// Create JSON file if it doesn't exist
51+
if (!files.includes(filename.replace('.md', '.json'))) {
52+
const jsonContent = JSON.stringify({
53+
content,
54+
metadata: {
55+
wordCount: content.split(/\s+/).length,
56+
charCount: content.length,
57+
timestamp: stats.mtime
58+
}
59+
}, null, 2)
60+
await fs.writeFile(jsonPath, jsonContent, 'utf-8')
61+
}
62+
63+
return {
64+
name: filename.replace('.md', ''),
65+
jsonPath,
66+
markdownPath: mdPath,
67+
timestamp: stats.mtime,
68+
size: stats.size,
69+
wordCount: content.split(/\s+/).length,
70+
charCount: content.length
9671
}
97-
return null
9872
})
99-
.filter((file): file is NonNullable<typeof file> => file !== null) // Type-safe filter
100-
.sort((a, b) => b.timestamp.getTime() - a.timestamp.getTime()) // Sort by newest first
101-
102-
return NextResponse.json({ files })
103-
}
104-
105-
// Load specific file
106-
const filename = getFilenameFromUrl(url)
107-
const filepath = path.join(STORAGE_DIR, filename)
108-
109-
if (!fs.existsSync(filepath)) {
110-
return NextResponse.json(
111-
{ error: 'No stored content found' },
112-
{ status: 404 }
11373
)
74+
75+
return NextResponse.json({
76+
success: true,
77+
files: fileDetails
78+
})
11479
}
115-
116-
const content = fs.readFileSync(filepath, 'utf-8')
117-
return NextResponse.json(JSON.parse(content))
80+
81+
// Handle single file request
82+
const filename = url
83+
.replace(/^https?:\/\//, '')
84+
.replace(/[^a-z0-9]/gi, '_')
85+
.toLowerCase() + '.md'
86+
87+
const filePath = path.join(STORAGE_DIR, filename)
88+
const content = await fs.readFile(filePath, 'utf-8')
89+
90+
return NextResponse.json({ success: true, content })
11891
} catch (error) {
119-
console.error('Error loading markdown:', error)
12092
return NextResponse.json(
121-
{ error: 'Failed to load markdown' },
93+
{ success: false, error: error instanceof Error ? error.message : 'Failed to load markdown' },
12294
{ status: 500 }
12395
)
12496
}

components/StoredFiles.tsx

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,11 +27,20 @@ export default function StoredFiles() {
2727
const listFiles = async () => {
2828
try {
2929
const response = await fetch('/api/storage')
30-
if (!response.ok) throw new Error('Failed to fetch files')
31-
const data = await response.json()
32-
setFiles(data.files)
30+
if (!response.ok) {
31+
const errorData = await response.json()
32+
throw new Error(errorData.error || 'Failed to fetch files')
33+
}
34+
35+
const { success, files } = await response.json()
36+
if (!success || !files) {
37+
throw new Error('Invalid response format')
38+
}
39+
40+
setFiles(files)
3341
} catch (error) {
3442
console.error('Error loading stored files:', error)
43+
setFiles([])
3544
} finally {
3645
setIsLoading(false)
3746
}

lib/storage.ts

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
export async function saveMarkdown(url: string, content: string) {
2+
try {
3+
const response = await fetch('/api/storage', {
4+
method: 'POST',
5+
headers: {
6+
'Content-Type': 'application/json',
7+
},
8+
body: JSON.stringify({ url, content }),
9+
})
10+
11+
if (!response.ok) {
12+
throw new Error('Failed to save markdown')
13+
}
14+
15+
return await response.json()
16+
} catch (error) {
17+
console.error('Error saving markdown:', error)
18+
return { success: false, error: error instanceof Error ? error.message : 'Failed to save markdown' }
19+
}
20+
}
21+
22+
export async function loadMarkdown(url: string) {
23+
try {
24+
const response = await fetch(`/api/storage?url=${encodeURIComponent(url)}`)
25+
26+
if (!response.ok) {
27+
throw new Error('Failed to load markdown')
28+
}
29+
30+
return await response.json()
31+
} catch (error) {
32+
console.error('Error loading markdown:', error)
33+
return { success: false, error: error instanceof Error ? error.message : 'Failed to load markdown' }
34+
}
35+
}

0 commit comments

Comments
 (0)