rewrite API #3
@ -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 /
|
@ -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
|
||||||
|
@ -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});
|
||||||
|
@ -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}`);
|
||||||
});
|
});
|
||||||
|
@ -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"));
|
||||||
|
@ -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": []
|
||||||
}
|
}
|
||||||
]
|
]
|
Reference in New Issue
Block a user