rewrite API #3

Merged
Sirherobrine23 merged 6 commits from v2APIs into main 2022-12-24 02:00:38 +00:00
10 changed files with 524 additions and 114 deletions
Showing only changes of commit 9f4b19aead - Show all commits

View File

@ -1,8 +1,8 @@
FROM debian:latest FROM debian:latest
ARG DEBIAN_FRONTEND="noninteractive" ARG DEBIAN_FRONTEND="noninteractive"
RUN apt update && \ RUN apt update && apt install -y jq curl
cd /tmp && mkdir debs && cd debs && \ WORKDIR /tmp/debs
apt-cache depends --recurse --no-recommends --no-suggests --no-conflicts --no-breaks --no-replaces --no-enhances curl wget | grep "^\w" | sort -u | xargs apt download RUN curl -Ssq https://api.github.com/repos/cli/cli/releases | grep "browser_download_url.*deb" | cut -d '"' -f 4 | xargs -n 1 curl -SsL -O
FROM scratch FROM scratch
COPY --from=0 /tmp/debs/*.deb / COPY --from=0 /tmp/debs/*.deb /

View File

@ -1,7 +1,7 @@
version: 1 version: 1
repos: repos: []
- repo: ghcr.io/sirherobrine23/nodeaptexample:sha256:c7b3eb9435f67aa08a2e98fdaa5c4548bb8e410fc804ed352bb03dcb212dde96 # - repo: ghcr.io/sirherobrine23/nodeaptexample:sha256:c7b3eb9435f67aa08a2e98fdaa5c4548bb8e410fc804ed352bb03dcb212dde96
from: oci # from: oci
# - repo: Sirherobrine23/ghcrAptRepository # - repo: Sirherobrine23/ghcrAptRepository
# from: release # from: release

View File

@ -3,6 +3,7 @@ import { extendsCrypto } from "@sirherobrine23/coreutils";
import { PassThrough, Readable } from "node:stream"; import { PassThrough, Readable } from "node:stream";
import { format } from "node:util"; import { format } from "node:util";
import { Compressor as lzmaCompress } from "lzma-native"; import { Compressor as lzmaCompress } from "lzma-native";
import { backendConfig } from "./repoConfig.js";
import express from "express"; import express from "express";
import zlib from "node:zlib"; import zlib from "node:zlib";
@ -14,7 +15,7 @@ type registerOobject = {
}[] }[]
}; };
export function packageManeger(RootOptions?: {origin?: string, lebel?: string}) { export function packageManeger(RootOptions?: backendConfig) {
const localRegister: registerOobject = {}; const localRegister: registerOobject = {};
function pushPackage(control: packageControl, getStream: () => Promise<Readable>, from?: string) { function pushPackage(control: packageControl, getStream: () => Promise<Readable>, from?: string) {
if (!localRegister[control.Package]) localRegister[control.Package] = []; if (!localRegister[control.Package]) localRegister[control.Package] = [];
@ -121,13 +122,12 @@ export function packageManeger(RootOptions?: {origin?: string, lebel?: string})
}); });
} }
async function createRelease(options?: {packageName?: string, Arch?: string, includesHashs?: boolean}) { async function createRelease(options?: {packageName?: string, Arch?: string, includesHashs?: boolean, component?: string[], archive?: string}) {
const textLines = [ const textLines = [];
`Lebel: ${RootOptions?.lebel||"node-apt"}`, if (RootOptions?.aptConfig?.origin) textLines.push(`Origin: ${RootOptions.aptConfig.origin}`);
`Date: ${new Date().toUTCString()}` if (options?.archive) textLines.push(`Archive: ${options?.archive}`);
]; else textLines.push(`Lebel: ${RootOptions?.aptConfig?.label||"node-apt"}`, `Date: ${new Date().toUTCString()}`);
const components = options?.component ?? ["main"];
const components = ["main"];
const archs: string[] = []; const archs: string[] = [];
if (options?.packageName) { if (options?.packageName) {
@ -141,20 +141,36 @@ export function packageManeger(RootOptions?: {origin?: string, lebel?: string})
textLines.push("Suite: all"); textLines.push("Suite: all");
} }
textLines.push(`Components: ${components.join(" ")}`, `Architectures: ${archs.join(" ")}`); textLines.push(`Components: ${options?.archive ? components[0] : components.join(" ")}`, `Architectures: ${archs.join(" ")}`);
if (options?.includesHashs) { if (options?.includesHashs && !options?.archive) {
const DataHashs = await Promise.all(archs.map(async (arch) => ({arch, hash: await packageHASH({Arch: arch})}))); const arrayComponents = [];
for (const component of components) {
for (const arch of archs) {
arrayComponents.push({
component,
arch,
});
}
}
const DataHashs = await Promise.all(arrayComponents.map(async (data) => {
return {
...data,
hash: await packageHASH({
packageName: options?.packageName,
})
};
}));
textLines.push(`MD5Sum:`); textLines.push(`MD5Sum:`);
DataHashs.forEach(data => { DataHashs.forEach(data => {
textLines.push(` ${data.hash.raw.md5} ${data.hash.raw.size} ${data.arch}/binary-${data.arch}/Packages`); textLines.push(` ${data.hash.raw.md5} ${data.hash.raw.size} ${data.component}/binary-${data.arch}/Packages`);
textLines.push(` ${data.hash.gz.md5} ${data.hash.gz.size} ${data.arch}/binary-${data.arch}/Packages.gz`); textLines.push(` ${data.hash.gz.md5} ${data.hash.gz.size} ${data.component}/binary-${data.arch}/Packages.gz`);
textLines.push(` ${data.hash.xz.md5} ${data.hash.xz.size} ${data.arch}/binary-${data.arch}/Packages.xz`); textLines.push(` ${data.hash.xz.md5} ${data.hash.xz.size} ${data.component}/binary-${data.arch}/Packages.xz`);
}); });
textLines.push(`SHA256:`); textLines.push(`SHA256:`);
DataHashs.forEach(data => { DataHashs.forEach(data => {
textLines.push(` ${data.hash.raw.sha256} ${data.hash.raw.size} ${data.arch}/binary-${data.arch}/Packages`); textLines.push(` ${data.hash.raw.sha256} ${data.hash.raw.size} ${data.component}/binary-${data.arch}/Packages`);
textLines.push(` ${data.hash.gz.sha256} ${data.hash.gz.size} ${data.arch}/binary-${data.arch}/Packages.gz`); textLines.push(` ${data.hash.gz.sha256} ${data.hash.gz.size} ${data.component}/binary-${data.arch}/Packages.gz`);
textLines.push(` ${data.hash.xz.sha256} ${data.hash.xz.size} ${data.arch}/binary-${data.arch}/Packages.xz`); textLines.push(` ${data.hash.xz.sha256} ${data.hash.xz.size} ${data.component}/binary-${data.arch}/Packages.xz`);
}); });
} }
textLines.push(""); textLines.push("");
@ -170,7 +186,7 @@ export function packageManeger(RootOptions?: {origin?: string, lebel?: string})
}; };
} }
export default async function repo(aptConfig: {}) { export default async function repo(aptConfig?: backendConfig) {
const app = express(); const app = express();
const registry = packageManeger(); const registry = packageManeger();
app.disable("x-powered-by").disable("etag").use(express.json()).use(express.urlencoded({extended: true})).use((_req, res, next) => { app.disable("x-powered-by").disable("etag").use(express.json()).use(express.urlencoded({extended: true})).use((_req, res, next) => {
@ -182,16 +198,32 @@ export default async function repo(aptConfig: {}) {
}); });
app.get("/", (_req, res) => res.json(registry.getPackages())); app.get("/", (_req, res) => res.json(registry.getPackages()));
app.get("/pool/:package/:arch/:version.deb", async (req, res) => {
const { package: packageName, arch, version } = req.params;
const packageData = registry.getPackages()[packageName];
if (!packageData) return res.status(404).json({error: "Package not found"});
const packageArch = packageData.filter((p) => p.control.Architecture === arch);
if (packageArch.length === 0) return res.status(404).json({error: "Package not found with the arch"});
const versionData = packageArch.find((p) => p.control.Version === version);
if (!versionData) return res.status(404).json({error: "Package not found with the version"});
if (!req.path.endsWith(".deb")) return res.json(versionData);
const stream = await versionData.getStream();
return stream.pipe(res.writeHead(200, {
"Content-Type": "application/octet-stream",
"Content-Length": versionData.control.Size,
}));
});
app.get("/sources.list", (req, res) => { app.get("/sources.list", (req, res) => {
const host = (req.socket.localPort && req.hostname) ? req.hostname+":"+req.socket.localPort : req.query.host||req.headers["x-forwarded-host"]||req.headers.host;
let Targets = ((req.query.t||req.query.targets) ?? ["all"]) as string[]|string;
if (typeof Targets === "string") Targets = Targets.split(",");
if (Targets?.length === 0) Targets = ["all"];
if (Targets?.includes("all")||Targets.some(a => a.toLowerCase().trim() === "all")) Targets = ["all"];
if (!Targets) Targets = ["all"];
res.setHeader("Content-type", "text/plain"); res.setHeader("Content-type", "text/plain");
let config = ""; let config = "";
if (req.query.all) config += format("deb [trusted=yes] %s://%s %s main\n", req.protocol, req.headers.host, "all"); for (const target of Targets) config += format(aptConfig?.aptConfig?.sourcesList ?? "deb [trusted=yes] %s://%s %s main\n", req.protocol, host, target);
else {
for (const suite of Object.keys(registry.getPackages())) {
config += format("deb [trusted=yes] %s://%s %s main\n", req.protocol, req.headers.host, suite);
}
}
res.send(config+"\n"); res.send(config+"\n");
}); });
@ -201,10 +233,24 @@ export default async function repo(aptConfig: {}) {
app.get("/dists/:suite/Release", (req, res, next) => { app.get("/dists/:suite/Release", (req, res, next) => {
const { suite } = req.params; const { suite } = req.params;
res.setHeader("Content-Type", "text/plain"); res.setHeader("Content-Type", "text/plain");
return registry.createRelease({packageName: suite === "all"?undefined:suite, includesHashs: true}).then((release) => res.send(release)).catch(next); return registry.createRelease({
includesHashs: true,
packageName: suite === "all"?undefined:suite,
}).then((release) => res.send(release)).catch(next);
}); });
// Components // Components
app.get("/dists/:suite/:component/binary-:arch/Release", (req, res, next) => {
const { suite, arch } = req.params;
res.setHeader("Content-Type", "text/plain");
return registry.createRelease({
includesHashs: aptConfig?.aptConfig?.enableHash ?? true,
Arch: arch,
packageName: suite === "all" ? undefined : suite,
component: [req.params.component],
archive: req.params.component,
}).then((release) => res.send(release)).catch(next);
});
app.get("/dists/:suite/:component/binary-:arch/Packages(.(gz|xz)|)", (req, res, next) => { app.get("/dists/:suite/:component/binary-:arch/Packages(.(gz|xz)|)", (req, res, next) => {
const {suite, arch} = req.params; const {suite, arch} = req.params;
const streams = registry.createPackages({packageName: suite, Arch: arch}); const streams = registry.createPackages({packageName: suite, Arch: arch});

View File

@ -12,7 +12,7 @@ yargs(process.argv.slice(2)).wrap(null).strict().help().option("cofig-path", {
type: "number", type: "number",
default: 3000, default: 3000,
}).parseAsync().then(async options => { }).parseAsync().then(async options => {
const { app, registry } = await repo({}); const { app, registry } = await repo();
app.listen(options.port, () => { app.listen(options.port, () => {
console.log(`Server listening on port ${options.port}`); console.log(`Server listening on port ${options.port}`);
}); });

View File

@ -22,6 +22,19 @@ export type configV1 = {
})[] })[]
}; };
export type backendConfig = {
aptConfig?: {
origin?: string,
label?: string,
enableHash?: boolean,
sourcesList?: string
},
repositorys: {
target: "oci_registry"|"github_release",
repo: string,
}[]
};
export async function getConfig(filePath: string): Promise<configV1> { export async function getConfig(filePath: string): Promise<configV1> {
if (!await coreUtils.extendFs.exists(filePath)) throw new Error("file not exists"); if (!await coreUtils.extendFs.exists(filePath)) throw new Error("file not exists");
const configData: configV1 = yaml.parse(await fs.readFile(filePath, "utf8")); const configData: configV1 = yaml.parse(await fs.readFile(filePath, "utf8"));

View File

@ -96,5 +96,103 @@
"headers": [], "headers": [],
"params": [], "params": [],
"tests": [] "tests": []
},
{
"_id": "d22e6c9a-65a2-42c5-8d9f-f5f593d82dd5",
"colId": "history",
"containerId": "",
"name": "http://localhost:3000/pool/",
"url": "http://localhost:3000/pool/",
"method": "GET",
"sortNum": 0,
"created": "2022-12-18T23:11:34.556Z",
"modified": "2022-12-18T23:11:34.556Z",
"headers": [],
"params": [],
"tests": []
},
{
"_id": "091394e2-824e-40a0-a4ee-435c940186df",
"colId": "history",
"containerId": "",
"name": "http://localhost:3000/pool/a",
"url": "http://localhost:3000/pool/a",
"method": "GET",
"sortNum": 0,
"created": "2022-12-18T23:11:43.135Z",
"modified": "2022-12-18T23:11:43.135Z",
"headers": [],
"params": [],
"tests": []
},
{
"_id": "d9daac8a-8c88-42e5-bed1-fe140c911c8a",
"colId": "history",
"containerId": "",
"name": "http://localhost:3000/pool/a/b",
"url": "http://localhost:3000/pool/a/b",
"method": "GET",
"sortNum": 0,
"created": "2022-12-18T23:13:15.489Z",
"modified": "2022-12-18T23:13:15.489Z",
"headers": [],
"params": [],
"tests": []
},
{
"_id": "7a618a4f-2f2e-4943-9120-ab1bebd6ebc9",
"colId": "history",
"containerId": "",
"name": "http://localhost:3000/pool/gh",
"url": "http://localhost:3000/pool/gh",
"method": "GET",
"sortNum": 0,
"created": "2022-12-18T23:18:38.398Z",
"modified": "2022-12-18T23:18:38.398Z",
"headers": [],
"params": [],
"tests": []
},
{
"_id": "453956ca-3122-4220-99ec-7a7533bfcd70",
"colId": "history",
"containerId": "",
"name": "http://localhost:3000/pool/gh/2.10.0",
"url": "http://localhost:3000/pool/gh/2.10.0",
"method": "GET",
"sortNum": 0,
"created": "2022-12-18T23:20:15.271Z",
"modified": "2022-12-18T23:20:15.271Z",
"headers": [],
"params": [],
"tests": []
},
{
"_id": "37a29f1f-25cf-4f9e-9246-71056ec87e1d",
"colId": "history",
"containerId": "",
"name": "http://localhost:3000/pool/gh/2.10.0.deb",
"url": "http://localhost:3000/pool/gh/2.10.0.deb",
"method": "GET",
"sortNum": 0,
"created": "2022-12-18T23:20:21.809Z",
"modified": "2022-12-18T23:20:21.809Z",
"headers": [],
"params": [],
"tests": []
},
{
"_id": "85562af1-6352-46ee-9b7f-10efed2e0782",
"colId": "history",
"containerId": "",
"name": "http://localhost:3000/pool/gh/amd64/2.10.0.deb",
"url": "http://localhost:3000/pool/gh/amd64/2.10.0.deb",
"method": "GET",
"sortNum": 0,
"created": "2022-12-18T23:23:22.999Z",
"modified": "2022-12-18T23:23:22.999Z",
"headers": [],
"params": [],
"tests": []
} }
] ]