Skip to content
This repository has been archived by the owner on Oct 29, 2023. It is now read-only.

Commit

Permalink
fix routes
Browse files Browse the repository at this point in the history
  • Loading branch information
Sirherobrine23 committed Mar 29, 2023
1 parent eb1bf7e commit 0e0d72b
Show file tree
Hide file tree
Showing 6 changed files with 205 additions and 171 deletions.
3 changes: 2 additions & 1 deletion .github/workflows/publish.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,8 @@ jobs:
sudo npm i -g semver
VERSION="$(semver -c ${{ github.ref_name }})"
echo "PACKAGE_VERSION=$VERSION" >> $GITHUB_ENV
jq --arg ver $VERSION '.version = $ver' package.json
jq --arg ver $VERSION '.version = $ver' package.json > package2.json
mv -fv package2.json package.json
# Install depencides and build
npm install --no-save
Expand Down
78 changes: 39 additions & 39 deletions src/aptServer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,25 @@ function returnUniq(arg1: string[]) {
export default function main(packageManeger: packageManeger, config: aptStreamConfig) {
const { gpgSign } = config;
const app = express.Router();
async function createPackage(packagesArray: packageData[], pathRoot: string, compress?: "gzip"|"lzma", callback: (stream: stream.Readable) => void = () => {}) {
const __stream = new stream.Readable({read() {}});
const comp = compress ? __stream.pipe(compress === "lzma" ? lzma.Compressor() : zlib.createGzip()) : null;
callback(comp ? comp : __stream);
return Promise.resolve().then(async () => {
for (let packIndex = 0; packIndex < packagesArray.length; packIndex++) {
if (packIndex !== 0) __stream.push(Buffer.from("\n\n"));
const { packageControl: control, packageComponent } = packagesArray[packIndex];
const hash = control.SHA1 || control.SHA256 || control.SHA512 || control.MD5sum;
if (!hash) continue;
__stream.push(Debian.createControl(Object.assign({
...control,
Filename: path.resolve("/", pathRoot ?? "", "pool", packageComponent ?? "main", `${hash}.deb`).slice(1),
})), "utf8");
}
__stream.push(null);
return extendsCrypto.createHashAsync(comp ? comp : __stream);
});
}

async function createRelease(packagesArr: packageData[], aptRoot: string) {
const { aptConfig } = config.repository[packagesArr.at(-1).packageDistribuition] ?? {};
Expand Down Expand Up @@ -105,63 +124,44 @@ export default function main(packageManeger: packageManeger, config: aptStreamCo
}, [] as string[]).join("\n");
}

function createPackage(packagesArray: packageData[], pathRoot: string, compress?: "gzip"|"lzma") {
const __stream = new stream.Readable({read() {}});
const comp = compress ? __stream.pipe(compress === "lzma" ? lzma.Compressor() : zlib.createGzip()) : null;
const __load = Promise.resolve().then(async () => {
for (let packIndex = 0; packIndex < packagesArray.length; packIndex++) {
if (packIndex !== 0) __stream.push(Buffer.from("\n\n"));
const { packageControl: control, packageComponent } = packagesArray[packIndex];
__stream.push(Debian.createControl(Object.assign({
...control,
Filename: path.resolve("/", pathRoot ?? "", "pool", packageComponent ?? "main", `${control.Package}_${control.Architecture}_${control.Version}.deb`),
})));
}
__stream.push(null);
return extendsCrypto.createHashAsync(comp ? comp : __stream);
});
return Object.assign(comp ? comp : __stream, __load);
}

app.get("/dists/:distName/((InRelease|Release(.gpg)?))", async (req, res) => {
app.get("/dists(|/.)/:distName/((InRelease|Release(.gpg)?))", async (req, res) => {
const packages = await packageManeger.search({packageDist: req.params["distName"]});
if (!packages.length) return res.status(404).json({error: "Distribuition not exsist"});
const Release = await createRelease(packages, path.resolve("/", path.posix.join(req.baseUrl, req.path), "../../.."));
if (req.path.endsWith("InRelease")||req.path.endsWith("Release.gpg")) {
let Release = await createRelease(packages, path.resolve("/", path.posix.join(req.baseUrl, req.path), "../../.."));
const lowerPath = req.path.toLowerCase();
if (lowerPath.endsWith("inrelease")||lowerPath.endsWith("release.gpg")) {
if (!gpgSign) return res.status(404).json({ error: "Repository not signed" });
const privateKey = gpgSign.authPassword ? await openpgp.decryptKey({privateKey: await openpgp.readPrivateKey({ armoredKey: gpgSign.private.content }), passphrase: gpgSign.authPassword}) : await openpgp.readPrivateKey({ armoredKey: gpgSign.private.content });
res.status(200).setHeader("Content-Type", "text/plain");
if (req.path.endsWith(".gpg")) return res.send(await openpgp.sign({
signingKeys: privateKey,
format: "armored",
message: await openpgp.createMessage({
text: Release
})
}));
return res.send(await openpgp.sign({signingKeys: privateKey, format: "armored", message: await openpgp.createCleartextMessage({text: Release})}));
if (req.path.endsWith(".gpg")) {
Release = Buffer.from(await openpgp.sign({
signingKeys: privateKey,
format: "armored",
message: await openpgp.createMessage({
text: Release
})
}) as any).toString("utf8");
} else Release = await openpgp.sign({signingKeys: privateKey, format: "armored", message: await openpgp.createCleartextMessage({text: Release})})
}
return res.status(200).setHeader("Content-Type", "text/plain").send(Release);
return res.status(200).setHeader("Content-Type", "text/plain").setHeader("Content-Length", String(Buffer.byteLength(Release))).send(Release);
});

app.get("/dists/:distName/:componentName/binary-:Arch/Packages(.(gz|xz))?", async (req, res) => {
app.get("/dists(|/.)/:distName/:componentName/binary-:Arch/Packages(.(gz|xz))?", async (req, res) => {
const { distName, componentName, Arch } = req.params;
const packages = await packageManeger.search({packageDist: distName, packageComponent: componentName, packageArch: Arch});
if (!packages.length) return res.status(404).json({error: "Distribuition not exsist"});
return createPackage(packages, path.resolve("/", path.posix.join(req.baseUrl, req.path), "../../../../.."), req.path.endsWith(".gzip") ? "gzip" : req.path.endsWith(".xz") ? "lzma" : undefined).pipe(res.writeHead(200, {
}));
return createPackage(packages, path.resolve("/", path.posix.join(req.baseUrl, req.path), "../../../../.."), req.path.endsWith(".gzip") ? "gzip" : req.path.endsWith(".xz") ? "lzma" : undefined, (str) => str.pipe(res.writeHead(200, {})));
});
app.get("/pool", async ({res}) => packageManeger.search({}).then(data => res.json(data)));
app.get("/pool/:componentName", async (req, res) => {
const packagesList = await packageManeger.search({packageComponent: req.params.componentName});
if (packagesList.length === 0) return res.status(404).json({error: "Package component not exists"});
return res.json(packagesList.map(({packageControl, packageDistribuition}) => ({control: packageControl, dist: packageDistribuition})));
});
app.get("/pool/:componentName/(:package)_(:arch)_(:version).deb", async (req, res, next) => {
const { componentName, package: packageName, arch, version: packageVersion } = req.params;
const packageID = (await packageManeger.search({packageComponent: componentName, packageArch: arch})).find(({packageControl: { Package, Version }}) => packageName === Package && Version === packageVersion);
app.get("/pool/:componentName/(:hash)(|.deb)", async (req, res, next) => {
const packageID = (await packageManeger.search({packageComponent: req.params.componentName})).find(({packageControl}) => ([String(packageControl.SHA1), String(packageControl.SHA256), String(packageControl.SHA512), String(packageControl.MD5sum)]).includes(req.params.hash));
if (!packageID) return res.status(404).json({error: "Package not exist"});
console.log(packageID);
return fileRestore(packageID, config).then(str => str.pipe(res.writeHead(200, {}))).catch(next);
if (req.path.endsWith(".deb")) return fileRestore(packageID, config).then(str => str.pipe(res.writeHead(200, {}))).catch(next);
return res.json(packageID.packageControl);
});
return app;
}
8 changes: 5 additions & 3 deletions src/configManeger.ts
Original file line number Diff line number Diff line change
Expand Up @@ -463,10 +463,12 @@ export default async function main(configPath: string, configOld?: aptStreamConf
console.log("Saving...");
await save(configPath, localConfig);
console.log("Now loading all packages");
const sync = new syncRepository();
sync.on("error", console.error);
const db = await connect(localConfig);
const sync = new syncRepository(db, localConfig);
sync.on("error", err => console.error(err?.message || err));
sync.on("addPackage", data => console.log("Added: %s -> %s/%s %s/%s", data.distName, data.componentName, data.control.Package, data.control.Version, data.control.Architecture, data.componentName));
return sync.sync(await connect(localConfig), localConfig);
await sync.wait();
await db.close();
}
return main(configPath, configOld);
}
Expand Down
86 changes: 54 additions & 32 deletions src/database.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import * as Debian from "@sirherobrine23/debian";
import { aptStreamConfig } from "./config.js";
import mongoDB from "mongodb";
import nano from "nano";
import { format } from "node:util";

export interface packageData {
packageComponent: string;
Expand All @@ -12,32 +13,66 @@ export interface packageData {
}

export interface packageManegerConfig {
getPackages(this: packageManeger): Promise<packageData[]>;
getPackages?(this: packageManeger): Promise<packageData[]>;
registryPackage?(this: packageManeger, ...args: Parameters<typeof packageManeger["prototype"]["addPackage"]>): ReturnType<typeof packageManeger["prototype"]["addPackage"]>;
findPackages?(this: packageManeger, search: {packageName?: string, packageArch?: string, packageComponent?: string, packageDist?: string}): Promise<packageData[]>;
findPackages?(this: packageManeger, search?: {packageName?: string, packageArch?: string, packageComponent?: string, packageDist?: string}): Promise<packageData[]>;
deleteSource?(this: packageManeger, id: string): Promise<void>;
close?(): Promise<void>;
}

export class packageManeger {
constructor(private options: packageManegerConfig) {}
#options?: packageManegerConfig
constructor(options?: packageManegerConfig) {this.#options = options || {}}
#internalPackage: packageData[] = [];
getPackages = async (): Promise<packageData[]> => {
if (typeof this.options.getPackages !== "function") throw new Error("Get packages disabled by Backend");
return this.options.getPackages.call(this);
if (typeof this.#options.getPackages !== "function") return this.#internalPackage.filter(data => !!data);
return this.#options.getPackages.call(this);
}

async search(search: {packageName?: string, packageArch?: string, packageComponent?: string, packageDist?: string}): Promise<packageData[]> {
if (typeof this.options.findPackages !== "function") return (await this.getPackages()).filter(data => ((!search.packageName) || (search.packageName === data.packageControl.Package)) && ((!search.packageArch) || (data.packageControl.Architecture === search.packageArch)) && ((!search.packageComponent) || (data.packageComponent === search.packageComponent)) && ((!search.packageDist) || (data.packageDistribuition === search.packageDist)));
return this.options.findPackages.call(this, search);
async search(search?: {packageName?: string, packageArch?: string, packageComponent?: string, packageDist?: string}): Promise<packageData[]> {
search ??= {};
if (typeof this.#options.findPackages !== "function") return (await this.getPackages()).filter(data => ((!search.packageName) || (search.packageName === data.packageControl.Package)) && ((!search.packageArch) || (data.packageControl.Architecture === search.packageArch)) && ((!search.packageComponent) || (data.packageComponent === search.packageComponent)) && ((!search.packageDist) || (data.packageDistribuition === search.packageDist)));
return this.#options.findPackages.call(this, search);
}

addPackage = async (distName: string, componentName: string, repoID: string, fileRestore: any, control: Debian.debianControl): Promise<{distName: string, componentName: string, control: Debian.debianControl}> => {
if (typeof this.options.registryPackage !== "function") throw new Error("Add package disabled");
if ((await this.search({
packageName: control.Package,
packageComponent: componentName,
packageArch: control.Architecture,
packageDist: distName
})).find(d => (d.packageControl.Version === control.Version))) throw new Error("Package exists!");
return this.options.registryPackage.call(this, distName, componentName, repoID, fileRestore, control);
})).find(d => (d.packageControl.Version === control.Version))) throw new Error(format("%s/%s_%s already exists registered!", control.Package, control.Architecture, control.Version));
if (typeof this.#options.registryPackage !== "function") this.#internalPackage.push({packageDistribuition: distName, packageComponent: componentName, id: repoID, fileRestore, packageControl: control});
else await Promise.resolve().then(() => this.#options.registryPackage.call(this, distName, componentName, repoID, fileRestore, control));
return {
componentName,
distName,
control
};
}

async deleteRepositorySource(id: string): Promise<void> {
if (typeof this.#options?.deleteSource === "function") {
await Promise.resolve().then(() => this.#options.deleteSource.call(this, id));
return;
}
for (const packIndex in this.#internalPackage) {
if (!this.#internalPackage[packIndex]) continue;
if (this.#internalPackage[packIndex].id !== id) continue;
delete this.#internalPackage[packIndex];
}
}
async close() {
if (typeof this.#options?.close === "function") await Promise.resolve().then(() => this.#options.close());
}
async Sync(config: aptStreamConfig): Promise<string[]> {
const packagesArray = await this.search();
const toDelete = packagesArray.filter(data => !(config.repository[data.packageDistribuition] && config.repository[data.packageDistribuition].source.find(rel => rel.id === data.id))).reduce((acc, data) => {
if (!acc.includes(data.id)) acc.push(data.id);
return acc;
}, [] as string[]);
for (const sourceID of toDelete) await this.deleteRepositorySource(sourceID);
return toDelete;
}
}

Expand Down Expand Up @@ -68,7 +103,10 @@ export async function connect(config: aptStreamConfig) {
distName,
control,
};
}
},
async deleteSource(id) {
await collection.findOneAndDelete({id});
},
});
} else if (database.drive === "couchdb") {
const nanoClient = nano(database.url);
Expand All @@ -94,27 +132,11 @@ export async function connect(config: aptStreamConfig) {
control
};
},
async deleteSource(id) {
await db.list({include_docs: true}).then(data => data.rows.map(({doc}) => doc)).then(docs => docs.filter((doc) => doc.id === id)).then(data => Promise.all(data.map(async doc => db.destroy(doc._id, doc._rev))));
},
});
}

const packagesStorage: packageData[] = [];
return new packageManeger({
async getPackages() {
return Array.from(packagesStorage);
},
async registryPackage(distName, componentName, repoID, fileRestore, control) {
packagesStorage.push({
packageDistribuition: distName,
packageComponent: componentName,
id: repoID,
packageControl: control,
fileRestore,
});
return {
componentName,
distName,
control
};
},
});
return new packageManeger();
}
6 changes: 4 additions & 2 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,10 +48,12 @@ yargs(process.argv.slice(2)).version(false).help(true).strictCommands().demandCo
const db = await connect(appConfig);
const app = express();
app.get("/", ({res}) => res.json({cluster: cluster.isWorker, id: cluster.worker?.id}));
app.get("/public(_key|)(|.gpg)", async ({res}) => {
app.get("/public(_key|)(|.gpg|.asc)", async (req, res) => {
if (!appConfig.gpgSign) return res.status(404).json({error: "Gpg not configured"});
// gpg --dearmor
if (req.path.endsWith(".asc")) return res.send(Buffer.from((await openpgp.unarmor(appConfig.gpgSign.public.content)).data as any));
const pubKey = (await openpgp.readKey({ armoredKey: appConfig.gpgSign.public.content })).armor();
return res.setHeader("Content-Type", "application/pgp-keys").send(pubKey);
return res.setHeader("Content-Type", "text/plain").send(pubKey);
});
const aptRoute = apt(db, appConfig);
app.use(aptRoute);
Expand Down
Loading

0 comments on commit 0e0d72b

Please sign in to comment.