This commit is contained in:
monoid 2023-06-12 10:37:52 +09:00
commit 8a67f4fbdb
2 changed files with 68 additions and 23 deletions

59
app.ts
View File

@ -2,18 +2,27 @@ import { Application, Router, isHttpError } from "https://deno.land/x/oak@v12.1.
import { import {
searchRepositoryWithTopic, searchRepositoryWithTopic,
getRepositoryTags, getRepositoryTags,
getRepositoryContent getRepositoryContent,
GiteaOption
} from "./gitea.ts"; } from "./gitea.ts";
import { ContentsResponse } from "./gitea_api.d.ts"; import { ContentsResponse } from "./gitea_api.d.ts";
import { Command } from "https://deno.land/x/cliffy@v0.25.7/mod.ts"; import { Command } from "https://deno.land/x/cliffy@v0.25.7/mod.ts";
// import { load } from "https://deno.land/std@0.181.0/dotenv/mod.ts"; import { load } from "https://deno.land/std@0.191.0/dotenv/mod.ts";
// const env = await load(); const env = await load();
function getGiteaOptions(): GiteaOption | undefined {
const token = env.TOKEN;
if (token === undefined) {
return undefined
}
return { token: token };
}
const app = new Application(); const app = new Application();
const router = new Router(); const router = new Router();
const RelativeTopic = "denolib"; const RelativeTopic = env.tag ?? "denolib";
export interface CompletionList { export interface CompletionList {
/** The list (or partial list) of completion items. */ /** The list (or partial list) of completion items. */
@ -37,7 +46,7 @@ router.get("/.well-known/deno-import-intellisense.json", (ctx) => {
"variables": [ "variables": [
{ {
"key": "package", "key": "package",
// "documentation": "/docs/packages/${package}", // "documentation": "/docs/packages/${package}",
"url": "/packages/${package}" "url": "/packages/${package}"
}, },
{ {
@ -46,7 +55,7 @@ router.get("/.well-known/deno-import-intellisense.json", (ctx) => {
}, },
{ {
"key": "path", "key": "path",
// "documentation": "/docs/packages/${package}/${{version}}/paths/${path}", // "documentation": "/docs/packages/${package}/${{version}}/paths/${path}",
"url": "/packages/${package}/${{version}}/paths/${path}" "url": "/packages/${package}/${{version}}/paths/${path}"
} }
] ]
@ -58,10 +67,11 @@ router.get("/.well-known/deno-import-intellisense.json", (ctx) => {
router.get("/packages/:package", async (ctx) => { router.get("/packages/:package", async (ctx) => {
const packageName = ctx.params.package; const packageName = ctx.params.package;
console.log(`searchRepositoryWithTopic: ${packageName}`); console.log(`searchRepositoryWithTopic: ${packageName}`);
const repositories = await searchRepositoryWithTopic(RelativeTopic); const options = getGiteaOptions();
const repositories = await searchRepositoryWithTopic(RelativeTopic, options);
const repo_name = repositories.data?.map((repo) => repo.full_name) const repo_name = repositories.data?.map((repo) => repo.full_name)
.filter(x => x !== undefined) .filter(x => x !== undefined)
.map(x=> x?.replace("/","@")) ?? []; .map(x => x?.replace("/", "@")) ?? [];
const completionList: CompletionList = { const completionList: CompletionList = {
items: repo_name as string[], items: repo_name as string[],
isIncomplete: true, // TODO: check if there are more than max results isIncomplete: true, // TODO: check if there are more than max results
@ -75,7 +85,8 @@ router.get("/packages/:package/versions", async (ctx) => {
const packageName = ctx.params.package; const packageName = ctx.params.package;
const [owner, repo] = packageName.split("@"); const [owner, repo] = packageName.split("@");
console.log(`getTags: owner: ${owner}, repo: ${repo}`); console.log(`getTags: owner: ${owner}, repo: ${repo}`);
const tags = await getRepositoryTags(owner, repo); const options = getGiteaOptions();
const tags = await getRepositoryTags(owner, repo, options);
const candidate = ["main", ...tags.map((tag) => tag.name) as string[]] const candidate = ["main", ...tags.map((tag) => tag.name) as string[]]
const completionList: CompletionList = { const completionList: CompletionList = {
items: candidate, items: candidate,
@ -92,7 +103,8 @@ router.get("/packages/:package/:version/paths/:path*", async (ctx) => {
const path = ctx.params.path; const path = ctx.params.path;
const [owner, repo] = packageName.split("@"); const [owner, repo] = packageName.split("@");
console.log(`getFilesEntry: owner: ${owner}, repo: ${repo}, path: ${path}, version: ${version}`); console.log(`getFilesEntry: owner: ${owner}, repo: ${repo}, path: ${path}, version: ${version}`);
const entries = await getRepositoryContent(owner, repo, path ?? "", version) as ContentsResponse[]; const options = getGiteaOptions();
const entries = await getRepositoryContent(owner, repo, path ?? "", version, options) as ContentsResponse[];
const completionList: CompletionList = { const completionList: CompletionList = {
items: entries.map((entry) => entry.name) as string[], items: entries.map((entry) => entry.name) as string[],
isIncomplete: false, isIncomplete: false,
@ -108,13 +120,14 @@ router.get("/:package([a-z0-9_]*@[a-z0-9_]*)/:version?/:path*", async (ctx) => {
const version = ctx.params.version; const version = ctx.params.version;
const path = ctx.params.path; const path = ctx.params.path;
console.log(`getFiles: owner: ${owner}, repo: ${repo}, path: ${path}, version: ${version}`); console.log(`getFiles: owner: ${owner}, repo: ${repo}, path: ${path}, version: ${version}`);
const entries = await getRepositoryContent(owner, repo, path ?? "", version); const options = getGiteaOptions();
const entries = await getRepositoryContent(owner, repo, path ?? "", version, options);
if (entries instanceof Array) { if (entries instanceof Array) {
ctx.response.type = "application/json"; ctx.response.type = "application/json";
ctx.response.body = entries; ctx.response.body = entries;
} }
else { else {
if ("errors" in entries){ if ("errors" in entries) {
ctx.throw(404); ctx.throw(404);
} }
// TODO: check if the file is text file or not (e.g. image) // TODO: check if the file is text file or not (e.g. image)
@ -123,11 +136,28 @@ router.get("/:package([a-z0-9_]*@[a-z0-9_]*)/:version?/:path*", async (ctx) => {
} }
}); });
app.use(async (ctx, next) => {
try {
await next();
}
catch (err) {
if (isHttpError(err)) {
console.log(err);
ctx.response.status = err.status;
const { message, status, stack } = err;
ctx.response.body = { message, status, stack };
}
else {
throw err;
}
}
});
app.use(router.routes()); app.use(router.routes());
app.use(router.allowedMethods()); app.use(router.allowedMethods());
app.use(async (ctx, next) => { app.use(async (ctx, next) => {
try{ try {
await next(); await next();
} }
catch (err) { catch (err) {
@ -137,7 +167,8 @@ app.use(async (ctx, next) => {
const { message, status, stack } = err; const { message, status, stack } = err;
ctx.response.body = { message, status, stack }; ctx.response.body = { message, status, stack };
} }
else { throw err; else {
throw err;
} }
} }
}); });

View File

@ -2,29 +2,43 @@ import { SearchResults, Tag, ContentsResponse } from "./gitea_api.d.ts";
const ENDPOINT_URL = "https://git.prelude.duckdns.org/api/v1/"; const ENDPOINT_URL = "https://git.prelude.duckdns.org/api/v1/";
export async function searchRepositoryWithTopic(topic: string): Promise<SearchResults> { export interface GiteaOption{
token: string;
}
async function fetchFromGitea(url: string | URL, giteaOption? :GiteaOption): Promise<any> {
const headers = new Headers();
if (giteaOption) {
headers.append("Authorization", "token " + giteaOption.token);
}
const response = await fetch(url, {
headers: headers
}
);
const data = await response.json();
return data;
}
export async function searchRepositoryWithTopic(topic: string, giteaOption? :GiteaOption): Promise<SearchResults> {
const url = new URL(ENDPOINT_URL+ "repos/search"); const url = new URL(ENDPOINT_URL+ "repos/search");
url.searchParams.append("q", topic); url.searchParams.append("q", topic);
url.searchParams.append("topic", "true"); url.searchParams.append("topic", "true");
const response = await fetch(url); const data = await fetchFromGitea(url,giteaOption);
const data = await response.json();
return data; return data;
} }
export async function getRepositoryTags(owner:string, export async function getRepositoryTags(owner:string,
repo:string): Promise<Tag[]>{ repo:string, giteaOption? :GiteaOption): Promise<Tag[]>{
const url = new URL(ENDPOINT_URL+ "repos/"+owner+"/"+repo+"/tags"); const url = new URL(ENDPOINT_URL+ "repos/"+owner+"/"+repo+"/tags");
const response = await fetch(url); const data = await fetchFromGitea(url,giteaOption);
const data = await response.json();
return data; return data;
} }
export async function getRepositoryContent(owner:string, export async function getRepositoryContent(owner:string,
repo:string, path:string, ref:string): Promise<ContentsResponse[] | ContentsResponse>{ repo:string, path:string, ref:string, giteaOption? :GiteaOption): Promise<ContentsResponse[] | ContentsResponse>{
const url = new URL(ENDPOINT_URL+ "repos/"+owner+"/"+repo+"/contents/"+path); const url = new URL(ENDPOINT_URL+ "repos/"+owner+"/"+repo+"/contents/"+path);
url.searchParams.append("ref", ref); url.searchParams.append("ref", ref);
const response = await fetch(url); const data = await fetchFromGitea(url,giteaOption);
const data = await response.json();
return data; return data;
} }