File size: 2,739 Bytes
68f7925
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
import fs from 'fs/promises';
import { NextRequest, NextResponse } from 'next/server';
import path from 'path';

const DATA_ROOT = '/data';

interface FileInfo {
  name: string;
  type: 'file' | 'directory';
  size: number;
  lastModified: string;
  path: string;
}

export async function GET(request: NextRequest) {
  try {
    const { searchParams } = new URL(request.url);
    const relativePath = searchParams.get('path') || '';

    // パストラバーサル対策
    const normalizedPath = path.normalize(relativePath).replace(/^(\.\.(\/|\\|$))+/, '');
    const targetPath = path.join(DATA_ROOT, normalizedPath);

    // /data配下であることを確認
    if (!targetPath.startsWith(DATA_ROOT)) {
      return NextResponse.json({ error: 'Invalid path' }, { status: 400 });
    }

    try {
      // ディレクトリの存在確認
      await fs.access(targetPath);
      const stat = await fs.stat(targetPath);

      if (!stat.isDirectory()) {
        return NextResponse.json({ error: 'Path is not a directory' }, { status: 400 });
      }

      // ディレクトリ内容を取得
      const entries = await fs.readdir(targetPath, { withFileTypes: true });

      const fileInfos: FileInfo[] = await Promise.all(
        entries.map(async (entry) => {
          const fullPath = path.join(targetPath, entry.name);
          const fileStat = await fs.stat(fullPath);
          const relativePath = path.relative(DATA_ROOT, fullPath);

          return {
            name: entry.name,
            type: entry.isDirectory() ? 'directory' : 'file',
            size: fileStat.size,
            lastModified: fileStat.mtime.toISOString(),
            path: `/${relativePath}`,
          } as FileInfo;
        }),
      );

      // ディレクトリを先に、その後ファイルを名前順にソート
      fileInfos.sort((a, b) => {
        if (a.type !== b.type) {
          return a.type === 'directory' ? -1 : 1;
        }
        return a.name.localeCompare(b.name);
      });

      return NextResponse.json({
        path: `/${normalizedPath}`,
        items: fileInfos,
      });
    } catch (error) {
      // /dataディレクトリが存在しない場合
      if ((error as NodeJS.ErrnoException).code === 'ENOENT') {
        return NextResponse.json(
          {
            path: `/${normalizedPath}`,
            items: [],
            message: '/dataディレクトリが存在しません(ローカル環境の場合は正常です)',
          },
          { status: 200 },
        );
      }
      throw error;
    }
  } catch (error) {
    console.error('[API /admin/data/list] Error:', error);
    return NextResponse.json({ error: 'Failed to read directory' }, { status: 500 });
  }
}