File size: 5,398 Bytes
7a5aa35
33ffc9c
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
cecb963
7a5aa35
3bd1a1b
7a5aa35
 
33ffc9c
 
 
7a5aa35
33ffc9c
7a5aa35
 
 
33ffc9c
7a5aa35
 
 
 
 
33ffc9c
 
7a5aa35
33ffc9c
 
7a5aa35
 
33ffc9c
 
 
 
7a5aa35
 
 
33ffc9c
 
 
 
cecb963
 
33ffc9c
cecb963
33ffc9c
 
 
 
cecb963
 
 
7a5aa35
33ffc9c
cecb963
33ffc9c
 
 
 
cecb963
 
 
 
33ffc9c
 
 
 
 
 
 
 
 
cecb963
 
33ffc9c
7a5aa35
 
33ffc9c
 
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
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
import { serve } from "bun";
import packages from "../database/database/packages.json" assert { type: "json" };
import programs from "../database/database/programs.json" assert { type: "json" };

const typedPackages: Item[] = packages as Item[];
const typedPrograms: Item[] = programs as Item[];

type Item = {
  name?: string;
  full_name?: string;
  owner?: string;
  description?: string | null;
  topics?: string[];
  created_at?: string;
  usage_count?: number;
};

const corsHeaders = {
  "Content-Type": "application/json",
  "Access-Control-Allow-Origin": "*",
};

const sortByDate = (a: Item, b: Item) =>
  new Date(b.created_at ?? "").getTime() - new Date(a.created_at ?? "").getTime();

const sortByUsage = (a: Item, b: Item) =>
  (b.usage_count ?? 0) - (a.usage_count ?? 0);

const sorted = {
  packages: {
    latest: [...typedPackages].sort(sortByDate),
    mostUsed: [...typedPackages].sort(sortByUsage),
  },
  programs: {
    latest: [...typedPrograms].sort(sortByDate),
    mostUsed: [...typedPrograms].sort(sortByUsage),
  },
};
    mostUsed: [...programs].sort(sortByUsage),
  },
};

function filterItems(items: Item[], q: string | null, filter: string | null) {
  return items.filter(({ name, full_name, description, topics }) => {
    if (filter && !topics?.some(t => t.toLowerCase() === filter)) return false;
    if (!q) return true;
    const qlc = q.toLowerCase();
    return [name, full_name, description, ...(topics ?? [])].some(
      field => field?.toLowerCase().includes(qlc)
    );
  });
}

function getPaginated(items: Item[], page = 0, size = 10) {
  return items.slice(page * size, page * size + size);
}

function findItem(items: Item[], owner: string, repo: string) {
  owner = owner.toLowerCase();
  repo = repo.toLowerCase();
  return items.find(
    ({ owner: o, name, full_name }) =>
      (o?.toLowerCase() === owner && name?.toLowerCase() === repo) ||
      full_name?.toLowerCase() === `${owner}/${repo}`
  );
}

function parseRange(str: string | null, max: number): [number, number] {
  const m = str?.match(/^(\d+)\.\.(\d+)$/);
  const [start, end] = m ? [parseInt(m[1]), parseInt(m[2])] : [0, 10];
  return [Math.max(0, start), Math.min(max, end)];
}

serve({
  port: 7860,
  fetch(req) {
    const url = new URL(req.url);
    const { pathname, searchParams } = url;
    const q = searchParams.get("q")?.trim().toLowerCase() ?? null;
    const filter = searchParams.get("filter")?.trim().toLowerCase() ?? null;

    if (req.method === "OPTIONS")
      return new Response(null, {
        status: 204,
        headers: {
          ...corsHeaders,
          "Access-Control-Allow-Methods": "GET, POST, OPTIONS",
          "Access-Control-Allow-Headers": "Content-Type",
        },
      });

    if (pathname === "/api/searchPackages")
      return Response.json(filterItems(packages, q, filter).slice(0, 25), { headers: corsHeaders });

    if (pathname === "/api/searchPrograms")
      return Response.json(filterItems(programs, q, filter).slice(0, 25), { headers: corsHeaders });

    if (pathname === "/api/infiniteScrollPackages") {
      const page = parseInt(searchParams.get("pageNumber") || "0", 10);
      if (isNaN(page) || page < 0)
        return Response.json({ error: "Invalid page number" }, { status: 400, headers: corsHeaders });
      return Response.json(getPaginated(packages, page), { headers: corsHeaders });
    }

    if (pathname === "/api/infiniteScrollPrograms") {
      const page = parseInt(searchParams.get("pageNumber") || "0", 10);
      if (isNaN(page) || page < 0)
        return Response.json({ error: "Invalid page number" }, { status: 400, headers: corsHeaders });
      return Response.json(getPaginated(programs, page), { headers: corsHeaders });
    }

    const programMatch = pathname.match(/^\/api\/programs\/([^/]+)\/([^/]+)$/);
    if (programMatch) {
      const [, owner, repo] = programMatch.map(s => s.toLowerCase());
      const found = findItem(programs, owner, repo);
      return Response.json(found || { error: "Program not found" }, {
        status: found ? 200 : 404,
        headers: corsHeaders,
      });
    }

    const packageMatch = pathname.match(/^\/api\/packages\/([^/]+)\/([^/]+)$/);
    if (packageMatch) {
      const [, owner, repo] = packageMatch.map(s => s.toLowerCase());
      const found = findItem(packages, owner, repo);
      return Response.json(found || { error: "Package not found" }, {
        status: found ? 200 : 404,
        headers: corsHeaders,
      });
    }

    if (pathname === "/api/indexDetailsPackages" || pathname === "/api/indexDetailsPrograms") {
      const section = searchParams.get("section");
      if (section !== "latestRepos" && section !== "mostUsed")
        return Response.json({ error: "Invalid section" }, { status: 400, headers: corsHeaders });
      const key = section === "latestRepos" ? "latest" : "mostUsed";
      const which = pathname.includes("Packages") ? "packages" : "programs";
      const data = sorted[which as "packages" | "programs"][key as "latest" | "mostUsed"] ?? (which === "packages" ? packages : programs);
      const [start, end] = parseRange(searchParams.get("range"), data.length);
      return Response.json(data.slice(start, end), { headers: corsHeaders });
    }

    return new Response("Not Found", { status: 404, headers: corsHeaders });
  },
});

console.log("Server running on http://localhost:7860");