Skip to content

Commit 0dc730c

Browse files
committed
server: fix gh repo ver check; add parser for alpine mirror
chore: pnpm up
1 parent 3ea4c62 commit 0dc730c

File tree

16 files changed

+254
-18167
lines changed

16 files changed

+254
-18167
lines changed

.npmrc

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
strict-peer-dependencies=true
22
auto-install-peers=true
33
legacy-peer-deps=false
4-
registry=https://registry.npmmirror.com/
4+
#registry=https://registry.npmmirror.com/
5+
registry=https://registry.npmjs.org
56
sharp_binary_host="https://npmmirror.com/mirrors/sharp"
67
sharp_libvips_binary_host="https://npmmirror.com/mirrors/sharp-libvips"

apps/dash/package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424
"zustand": "^4.3.7"
2525
},
2626
"devDependencies": {
27-
"@types/react": "^18.0.38",
28-
"@types/react-dom": "^18.0.11"
27+
"@types/react": "^18.2.0",
28+
"@types/react-dom": "^18.2.1"
2929
}
3030
}

apps/demo/package.json

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
"start": "next start"
1616
},
1717
"dependencies": {
18-
"@floating-ui/dom": "^1.2.6",
18+
"@floating-ui/dom": "^1.2.7",
1919
"@floating-ui/react": "^0.23.1",
2020
"@headlessui/react": "^1.7.14",
2121
"@lexical/clipboard": "^0.5.0",
@@ -63,10 +63,10 @@
6363
"@tiptap/react": "2.0.0-beta.209",
6464
"@tiptap/starter-kit": "2.0.0-beta.209",
6565
"@tiptap/suggestion": "2.0.0-beta.209",
66-
"@trpc/client": "^10.21.1",
67-
"@trpc/next": "^10.21.1",
68-
"@trpc/react-query": "^10.21.1",
69-
"@trpc/server": "^10.21.1",
66+
"@trpc/client": "^10.21.2",
67+
"@trpc/next": "^10.21.2",
68+
"@trpc/react-query": "^10.21.2",
69+
"@trpc/server": "^10.21.2",
7070
"@types/ua-parser-js": "^0.7.36",
7171
"@wener/data": "workspace:^",
7272
"@wener/reaction": "workspace:^",
@@ -84,7 +84,7 @@
8484
"classnames": "^2.3.2",
8585
"common": "workspace:^",
8686
"cookie": "^0.5.0",
87-
"daisyui": "^2.51.5",
87+
"daisyui": "^2.51.6",
8888
"dayjs": "^1.11.7",
8989
"domhandler": "^5.0.3",
9090
"eta": "^2.0.1",
@@ -104,7 +104,7 @@
104104
"prosemirror-markdown": "^1.10.1",
105105
"prosemirror-model": "^1.19.0",
106106
"prosemirror-state": "^1.4.2",
107-
"prosemirror-view": "^1.30.2",
107+
"prosemirror-view": "^1.31.0",
108108
"qrcode": "^1.5.3",
109109
"qrcode-reader": "^1.0.4",
110110
"react": "^18.2.0",
@@ -116,14 +116,14 @@
116116
"styled-components": "^5.3.10",
117117
"sucrase": "^3.32.0",
118118
"superjson": "^1.12.3",
119-
"tailwindcss": "^3.3.1",
119+
"tailwindcss": "^3.3.2",
120120
"tar": "^6.1.13",
121121
"tippy.js": "^6.3.7",
122122
"ua-parser-js": "^1.0.35",
123123
"use-immer": "^0.9.0",
124124
"utility-types": "^3.10.0",
125125
"valtio": "^1.10.4",
126-
"yaml": "^2.2.1",
126+
"yaml": "^2.2.2",
127127
"yjs": "^13.6.0",
128128
"zod": "^3.21.4",
129129
"zustand": "^4.3.7"
@@ -133,9 +133,9 @@
133133
"@types/lodash": "^4.14.194",
134134
"@types/markdown-it": "^12.2.3",
135135
"@types/qrcode": "^1.5.0",
136-
"@types/react": "^18.0.38",
137-
"@types/react-dom": "^18.0.11",
138-
"@types/react-is": "^17.0.3",
136+
"@types/react": "^18.2.0",
137+
"@types/react-dom": "^18.2.1",
138+
"@types/react-is": "^17.0.4",
139139
"@types/react-test-renderer": "^18.0.0",
140140
"@types/styled-components": "^5.1.26",
141141
"@types/tar": "^6.1.4",

apps/server/package.json

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,11 @@
1515
"@fastify/cookie": "^8.3.0",
1616
"@fastify/helmet": "^10.1.0",
1717
"@fastify/static": "^6.10.1",
18-
"@fastify/websocket": "^7.2.0",
18+
"@fastify/websocket": "^8.0.0",
1919
"@mercuriusjs/gateway": "^1.2.0",
20-
"@mikro-orm/core": "^5.6.16",
20+
"@mikro-orm/core": "^5.7.2",
2121
"@mikro-orm/nestjs": "^5.1.8",
22-
"@mikro-orm/postgresql": "^5.6.16",
22+
"@mikro-orm/postgresql": "^5.7.2",
2323
"@nest-lab/fastify-multer": "^1.1.0",
2424
"@nestjs/cache-manager": "^1.0.0",
2525
"@nestjs/common": "^9.4.0",
@@ -36,42 +36,44 @@
3636
"@wener/utils": "workspace:^",
3737
"@zxing/library": "^0.20.0",
3838
"axios": "^1.3.6",
39-
"cache-manager": "^5.2.0",
39+
"cache-manager": "^5.2.1",
4040
"class-transformer": "^0.5.1",
4141
"class-validator": "^0.14.0",
4242
"dayjs": "^1.11.7",
4343
"dotenv": "^16.0.3",
4444
"eventsource-parser": "^1.0.0",
45-
"fastify": "^4.15.0",
45+
"fastify": "^4.17.0",
4646
"fastify-multer": "^2.0.3",
4747
"fs-extra": "^11.1.1",
4848
"generate-password": "^1.7.0",
4949
"graphql": "^16.6.0",
5050
"graphql-ws": "^5.12.1",
5151
"i18next": "^22.4.15",
5252
"jimp": "^0.22.7",
53-
"jose": "^4.14.1",
53+
"jose": "^4.14.3",
5454
"jsdom": "^21.1.1",
5555
"lodash": "^4.17.21",
5656
"lru-cache": "^9.1.1",
57-
"mercurius": "^12.2.0",
57+
"mercurius": "^13.0.0",
5858
"object-hash": "^3.0.0",
5959
"qrcode": "^1.5.3",
6060
"reflect-metadata": "^0.1.13",
6161
"semver": "^7.5.0",
62+
"tar": "^6.1.13",
6263
"valtio": "^1.10.4",
6364
"zod": "^3.21.4",
6465
"zxcvbn": "^4.4.2"
6566
},
6667
"devDependencies": {
6768
"@nestjs/testing": "^9.4.0",
6869
"@swc/cli": "^0.1.62",
69-
"@swc/core": "^1.3.53",
70+
"@swc/core": "^1.3.55",
7071
"@types/fs-extra": "^11.0.1",
7172
"@types/lodash": "^4.14.194",
7273
"@types/object-hash": "^3.0.2",
7374
"@types/qrcode": "^1.5.0",
7475
"@types/semver": "^7.3.13",
76+
"@types/tar": "^6.1.4",
7577
"@types/zxcvbn": "^4.4.1"
7678
}
7779
}
Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
import { Readable } from 'node:stream';
2+
import tar from 'tar';
3+
4+
export class Mirror {
5+
url = 'https://mirrors.tuna.tsinghua.edu.cn/alpine/';
6+
7+
getRepoIndexUrl({ ver, repo, arch }: RepoCoordinate) {
8+
return `${this.url}/v${ver}/${repo}/${arch}/APKINDEX.tar.gz`;
9+
}
10+
}
11+
12+
export interface RepoCoordinate {
13+
ver: string | 'edge';
14+
repo: string;
15+
arch: string;
16+
}
17+
18+
const Arch = ['x86_64', 'x86', 'aarch64', 'armhf', 'ppc64le', 's390x', 'armv7', 'riscv64'];
19+
const Repo = ['main', 'community', 'testing'];
20+
21+
const Entry: Record<string, { name: string; parse?: (v: string) => any }> = {
22+
P: { name: 'name' },
23+
V: { name: 'version' },
24+
A: { name: 'arch' },
25+
U: { name: 'url' },
26+
L: { name: 'license' },
27+
C: { name: 'checksum' },
28+
T: { name: 'description' },
29+
S: { name: 'size', parse: (v: string) => parseInt(v, 10) },
30+
I: { name: 'installSize', parse: (v: string) => parseInt(v, 10) },
31+
o: { name: 'origin' },
32+
m: { name: 'maintainer' },
33+
D: { name: 'depends', parse: (v: string) => v.split(' ') },
34+
p: { name: 'provides', parse: (v: string) => v.split(' ') },
35+
i: { name: 'installIf', parse: (v: string) => v.split(' ') },
36+
t: { name: 'buildTime', parse: (v: string) => new Date(parseInt(v, 10) * 1000) },
37+
c: { name: 'commit' },
38+
k: { name: 'providerPriority', parse: (v: string) => parseInt(v, 10) },
39+
};
40+
41+
interface ApkIndexPackageEntry {
42+
name: string;
43+
version: string;
44+
arch: string;
45+
url: string;
46+
license: string;
47+
description: string;
48+
size: number;
49+
installSize: number;
50+
origin: string;
51+
maintainer: string;
52+
depends: string[];
53+
provides: string[];
54+
installIf: string[];
55+
buildTime: Date;
56+
commit: string;
57+
checksum: string;
58+
providerPriority: number;
59+
}
60+
61+
export function parseApkIndexContent(content: string) {
62+
// // order based on apk-tools apk_pkg_write_index_entry
63+
// https://wiki.alpinelinux.org/wiki/Apk_spec
64+
const packageInfoLines = content.split('\n');
65+
const packages: ApkIndexPackageEntry[] = [];
66+
67+
let cur: Record<string, any> = {};
68+
for (const line of packageInfoLines) {
69+
const idx = line.indexOf(':');
70+
const key = line.slice(0, idx);
71+
const value = line.slice(idx + 1).trim();
72+
if (!line) {
73+
if (Object.keys(cur).length > 0) {
74+
packages.push(cur as any);
75+
cur = {};
76+
}
77+
continue;
78+
}
79+
80+
const entry = Entry[key as keyof typeof Entry];
81+
if (!entry) {
82+
throw new Error(`Unknown entry: ${key} in line "${line}"`);
83+
}
84+
cur[entry.name] = entry.parse?.(value) ?? value;
85+
}
86+
return packages;
87+
}
88+
89+
export function parseApkIndexArchive(rs: Readable) {
90+
let apkIndexContent = '';
91+
return new Promise<{ content: string }>((resolve, reject) => {
92+
rs.pipe(tar.t())
93+
.on('entry', (entry) => {
94+
if (entry.path === 'APKINDEX') {
95+
entry.on('data', (data) => {
96+
apkIndexContent += data.toString();
97+
});
98+
}
99+
})
100+
.on('end', () => resolve({ content: apkIndexContent }))
101+
.on('error' as any, () => reject());
102+
});
103+
}
104+
105+
export function parseChecksum(s: string): { type: 'none' | 'sha1' | 'md5'; sum?: Uint8Array } {
106+
const c: { type: 'none' | 'sha1' | 'md5'; sum?: Uint8Array } = {
107+
type: 'none',
108+
};
109+
110+
if (s === '') {
111+
return c;
112+
}
113+
114+
if (s.length < 2) {
115+
throw new Error(`ParseChecksum: invalid checksum size ${s.length}`);
116+
}
117+
118+
const enc = s[0];
119+
const typ = s[1];
120+
121+
switch (enc) {
122+
case 'X':
123+
c.sum = new Uint8Array(
124+
s
125+
.slice(2)
126+
.match(/.{1,2}/g)!
127+
.map((byte) => parseInt(byte, 16)),
128+
);
129+
break;
130+
case 'Q':
131+
c.sum = new Uint8Array(
132+
atob(s.slice(2))
133+
.split('')
134+
.map((c) => c.charCodeAt(0)),
135+
);
136+
break;
137+
}
138+
139+
switch (typ) {
140+
case '1':
141+
c.type = 'sha1';
142+
break;
143+
default:
144+
c.type = 'md5';
145+
break;
146+
}
147+
148+
return c;
149+
}

apps/server/src/apps/apis-open-server/github/repo.controller.ts

Lines changed: 40 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,45 @@
1+
import { Transform } from 'class-transformer';
2+
import { IsBoolean, IsBooleanString, IsOptional } from 'class-validator';
13
import { type SemVer } from 'semver';
24
import semver from 'semver/preload';
3-
import { Controller, Get, Param } from '@nestjs/common';
4-
import { ApiTags } from '@nestjs/swagger';
5+
import { Controller, Get, Param, Query, UsePipes, ValidationPipe } from '@nestjs/common';
6+
import { ApiProperty, ApiTags } from '@nestjs/swagger';
57
import { Octokit } from '@octokit/rest';
68
import { requireFound } from '../../../app/util/requireFound';
79

10+
class VersionFilter {
11+
@ApiProperty({ required: false })
12+
@IsOptional()
13+
@IsBoolean()
14+
@Transform(({ value }) => value === 'true' || value === true)
15+
prerelease: boolean = false;
16+
17+
@ApiProperty({ required: false })
18+
@IsOptional()
19+
@IsBoolean()
20+
@Transform(({ value }) => value === 'true' || value === true)
21+
loose: boolean = false;
22+
23+
@ApiProperty({ required: false })
24+
range?: string;
25+
26+
@ApiProperty({
27+
enum: ['only', 'ignore'],
28+
required: false,
29+
})
30+
calver?: string;
31+
}
32+
833
@ApiTags('GitHub')
934
@Controller('/github/r/:owner/:repo')
1035
export class RepoController {
1136
constructor(readonly octokit: Octokit) {}
1237

1338
@Get('version')
14-
async version(@Param('owner') owner: string, @Param('repo') repo: string) {
39+
@UsePipes(new ValidationPipe({ transform: true }))
40+
async version(@Param('owner') owner: string, @Param('repo') repo: string, @Query() filter: VersionFilter) {
1541
const { octokit } = this;
16-
const tags = await repoTags({ owner, repo, octokit });
42+
const tags = await repoTags({ ...filter, owner, repo, octokit });
1743
const items = tags.map(({ name, version, commit, calver }) => ({
1844
tag: name,
1945
version: version.format(),
@@ -23,15 +49,17 @@ export class RepoController {
2349
major: version.major,
2450
minor: version.minor,
2551
patch: version.patch,
52+
build: version.build,
53+
prerelease: version.prerelease,
2654
},
2755
}));
2856
return requireFound(items?.[0]);
2957
}
3058

3159
@Get('tag')
32-
async listTag(@Param('owner') owner: string, @Param('repo') repo: string) {
60+
async listTag(@Param('owner') owner: string, @Param('repo') repo: string, @Query() filter: VersionFilter) {
3361
const { octokit } = this;
34-
const tags = await repoTags({ owner, repo, octokit });
62+
const tags = await repoTags({ ...filter, owner, repo, octokit });
3563
const items = tags.map(({ name, version, commit }) => ({ name, version: version.format(), commit: commit.sha }));
3664
return { items };
3765
}
@@ -62,7 +90,7 @@ async function repoTags({
6290
.map((v) => {
6391
return {
6492
...v,
65-
version: semver.coerce(v.name) as SemVer,
93+
version: (semver.parse(v.name) || semver.coerce(v.name)) as SemVer,
6694
};
6795
})
6896
.filter((v) => v.version)
@@ -82,6 +110,11 @@ async function repoTags({
82110
} else if (calver === 'ignore') {
83111
tags = tags.filter((v) => !v.calver);
84112
}
113+
if (prerelease === true) {
114+
tags = tags.filter((v) => v.version.prerelease.length > 0);
115+
} else if (prerelease === false) {
116+
tags = tags.filter((v) => v.version.prerelease.length === 0);
117+
}
85118
if (range) {
86119
tags = tags.filter((v) =>
87120
semver.satisfies(v.version, range, {

0 commit comments

Comments
 (0)