* add support for idmapped mounts to start-sdk

* misc fixes

* misc fixes

* add default to textarea

* fix iptables masquerade rule

* fix textarea types

* more fixes

* better logging for rsync

* fix tty size

* fix wg conf generation for android

* disable file mounts on dependencies

* mostly there, some styling issues (#3069)

* mostly there, some styling issues

* fix: address comments (#3070)

* fix: address comments

* fix: fix

* show SSL for any address with secure protocol and ssl added

* better sorting and messaging

---------

Co-authored-by: Alex Inkin <alexander@inkin.ru>

* fixes for nextcloud

* allow sidebar navigation during service state traansitions

* wip: x-forwarded headers

* implement x-forwarded-for proxy

* lowercase domain names and fix warning popover bug

* fix http2 websockets

* fix websocket retry behavior

* add arch filters to s9pk pack

* use docker for start-cli install

* add version range to package signer on registry

* fix rcs < 0

* fix user information parsing

* refactor service interface getters

* disable idmaps

* build fixes

* update docker login action

* streamline build

* add start-cli workflow

* rename

* riscv64gc

* fix ui packing

* no default features on cli

* make cli depend on GIT_HASH

* more build fixes

* more build fixes

* interpolate arch within dockerfile

* fix tests

* add launch ui to service page plus other small improvements (#3075)

* add launch ui to service page plus other small improvements

* revert translation disable

* add spinner to service list if service is health and loading

* chore: some visual tune up

* chore: update Taiga UI

---------

Co-authored-by: waterplea <alexander@inkin.ru>

* fix backups

* feat: use arm hosted runners and don't fail when apt package does not exist (#3076)

---------

Co-authored-by: Matt Hill <mattnine@protonmail.com>
Co-authored-by: Shadowy Super Coder <musashidisciple@proton.me>
Co-authored-by: Matt Hill <MattDHill@users.noreply.github.com>
Co-authored-by: Alex Inkin <alexander@inkin.ru>
Co-authored-by: Remco Ros <remcoros@live.nl>
This commit is contained in:
Aiden McClelland
2025-12-15 13:30:50 -07:00
committed by GitHub
parent b945243d1a
commit 0430e0f930
148 changed files with 2572 additions and 1761 deletions

View File

@@ -109,11 +109,9 @@ export class DropPromise<T> implements Promise<T> {
}
}
export class DropGenerator<
T = unknown,
TReturn = any,
TNext = unknown,
> implements AsyncGenerator<T, TReturn, TNext> {
export class DropGenerator<T = unknown, TReturn = any, TNext = unknown>
implements AsyncGenerator<T, TReturn, TNext>
{
private static dropFns: { [id: number]: () => void } = {}
private static registry = new FinalizationRegistry((id: number) => {
const drop = DropGenerator.dropFns[id]

View File

@@ -19,7 +19,7 @@ export const getHostname = (url: string): Hostname | null => {
type FilterKinds =
| "onion"
| "local"
| "mdns"
| "domain"
| "ip"
| "ipv4"
@@ -42,10 +42,10 @@ type VisibilityFilter<V extends "public" | "private"> = V extends "public"
: never
type KindFilter<K extends FilterKinds> = K extends "onion"
? (HostnameInfo & { kind: "onion" }) | KindFilter<Exclude<K, "onion">>
: K extends "local"
: K extends "mdns"
?
| (HostnameInfo & { kind: "ip"; hostname: { kind: "local" } })
| KindFilter<Exclude<K, "local">>
| KindFilter<Exclude<K, "mdns">>
: K extends "domain"
?
| (HostnameInfo & { kind: "ip"; hostname: { kind: "domain" } })
@@ -80,11 +80,17 @@ type FilterReturnTy<F extends Filter> = F extends {
: Exclude<HostnameInfo, FilterReturnTy<E>>
: HostnameInfo
const defaultFilter = {
const nonLocalFilter = {
exclude: {
kind: ["localhost", "link-local"] as ("localhost" | "link-local")[],
},
}
} as const
const publicFilter = {
visibility: "public",
} as const
const onionFilter = {
kind: "onion",
} as const
type Formats = "hostname-info" | "urlstring" | "url"
type FormatReturnTy<
@@ -98,7 +104,7 @@ type FormatReturnTy<
? UrlString | FormatReturnTy<F, Exclude<Format, "urlstring">>
: never
export type Filled = {
export type Filled<F extends Filter = {}> = {
hostnames: HostnameInfo[]
toUrls: (h: HostnameInfo) => {
@@ -106,30 +112,17 @@ export type Filled = {
sslUrl: UrlString | null
}
filter: <
F extends Filter = typeof defaultFilter,
Format extends Formats = "urlstring",
>(
filter?: F,
format: <Format extends Formats = "urlstring">(
format?: Format,
) => FormatReturnTy<F, Format>[]
) => FormatReturnTy<{}, Format>[]
publicHostnames: HostnameInfo[]
onionHostnames: HostnameInfo[]
localHostnames: HostnameInfo[]
ipHostnames: HostnameInfo[]
ipv4Hostnames: HostnameInfo[]
ipv6Hostnames: HostnameInfo[]
nonIpHostnames: HostnameInfo[]
filter: <NewFilter extends Filter>(
filter: NewFilter,
) => Filled<NewFilter & Filter>
urls: UrlString[]
publicUrls: UrlString[]
onionUrls: UrlString[]
localUrls: UrlString[]
ipUrls: UrlString[]
ipv4Urls: UrlString[]
ipv6Urls: UrlString[]
nonIpUrls: UrlString[]
nonLocal: Filled<typeof nonLocalFilter & Filter>
public: Filled<typeof publicFilter & Filter>
onion: Filled<typeof onionFilter & Filter>
}
export type FilledAddressInfo = AddressInfo & Filled
export type ServiceInterfaceFilled = {
@@ -225,7 +218,7 @@ function filterRec(
(h) =>
invert !==
((kind.has("onion") && h.kind === "onion") ||
(kind.has("local") &&
(kind.has("mdns") &&
h.kind === "ip" &&
h.hostname.kind === "local") ||
(kind.has("domain") &&
@@ -258,86 +251,45 @@ export const filledAddress = (
}
const hostnames = host.hostnameInfo[addressInfo.internalPort] ?? []
return {
...addressInfo,
hostnames,
toUrls,
filter: <
F extends Filter = typeof defaultFilter,
Format extends Formats = "urlstring",
>(
filter?: F,
format?: Format,
) => {
const filtered = filterRec(hostnames, filter ?? defaultFilter, false)
let res: FormatReturnTy<F, Format>[] = filtered as any
if (format === "hostname-info") return res
const urls = filtered.flatMap(toUrlArray)
if (format === "url") res = urls.map((u) => new URL(u)) as any
else res = urls as any
return res
},
get publicHostnames() {
return hostnames.filter((h) => h.kind === "onion" || h.public)
},
get onionHostnames() {
return hostnames.filter((h) => h.kind === "onion")
},
get localHostnames() {
return hostnames.filter(
(h) => h.kind === "ip" && h.hostname.kind === "local",
)
},
get ipHostnames() {
return hostnames.filter(
(h) =>
h.kind === "ip" &&
(h.hostname.kind === "ipv4" || h.hostname.kind === "ipv6"),
)
},
get ipv4Hostnames() {
return hostnames.filter(
(h) => h.kind === "ip" && h.hostname.kind === "ipv4",
)
},
get ipv6Hostnames() {
return hostnames.filter(
(h) => h.kind === "ip" && h.hostname.kind === "ipv6",
)
},
get nonIpHostnames() {
return hostnames.filter(
(h) =>
h.kind === "ip" &&
h.hostname.kind !== "ipv4" &&
h.hostname.kind !== "ipv6",
)
},
get urls() {
return this.hostnames.flatMap(toUrlArray)
},
get publicUrls() {
return this.publicHostnames.flatMap(toUrlArray)
},
get onionUrls() {
return this.onionHostnames.flatMap(toUrlArray)
},
get localUrls() {
return this.localHostnames.flatMap(toUrlArray)
},
get ipUrls() {
return this.ipHostnames.flatMap(toUrlArray)
},
get ipv4Urls() {
return this.ipv4Hostnames.flatMap(toUrlArray)
},
get ipv6Urls() {
return this.ipv6Hostnames.flatMap(toUrlArray)
},
get nonIpUrls() {
return this.nonIpHostnames.flatMap(toUrlArray)
},
function filledAddressFromHostnames<F extends Filter>(
hostnames: HostnameInfo[],
): Filled<F> & AddressInfo {
return {
...addressInfo,
hostnames,
toUrls,
format: <Format extends Formats = "urlstring">(format?: Format) => {
let res: FormatReturnTy<{}, Format>[] = hostnames as any
if (format === "hostname-info") return res
const urls = hostnames.flatMap(toUrlArray)
if (format === "url") res = urls.map((u) => new URL(u)) as any
else res = urls as any
return res
},
filter: <NewFilter extends Filter>(filter: NewFilter) => {
return filledAddressFromHostnames<NewFilter & F>(
filterRec(hostnames, filter, false),
)
},
get nonLocal(): Filled<typeof nonLocalFilter & F> {
return filledAddressFromHostnames<typeof nonLocalFilter & F>(
filterRec(hostnames, nonLocalFilter, false),
)
},
get public(): Filled<typeof publicFilter & F> {
return filledAddressFromHostnames<typeof publicFilter & F>(
filterRec(hostnames, publicFilter, false),
)
},
get onion(): Filled<typeof onionFilter & F> {
return filledAddressFromHostnames<typeof onionFilter & F>(
filterRec(hostnames, onionFilter, false),
)
},
}
}
return filledAddressFromHostnames<{}>(hostnames)
}
const makeInterfaceFilled = async ({

View File

@@ -18,6 +18,9 @@ export class ComposableRegex {
}
}
export const escapeLiteral = (str: string) =>
str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")
// https://ihateregex.io/expr/ipv6/
export const ipv6 = new ComposableRegex(
/(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))/,
@@ -69,3 +72,13 @@ export const emailWithName = new ComposableRegex(
export const base64 = new ComposableRegex(
/(?:[a-zA-Z0-9+\/]{4})*(?:|(?:[a-zA-Z0-9+\/]{3}=)|(?:[a-zA-Z0-9+\/]{2}==)|(?:[a-zA-Z0-9+\/]{1}===))/,
)
//https://rgxdb.com/r/1NUN74O6
export const base64Whitespace = new ComposableRegex(
/(?:([a-zA-Z0-9+\/]\s*){4})*(?:|(?:([a-zA-Z0-9+\/]\s*){3}=)|(?:([a-zA-Z0-9+\/]\s*){2}==)|(?:([a-zA-Z0-9+\/]\s*){1}===))/,
)
export const pem = (label: string) =>
new ComposableRegex(
`-----BEGIN ${escapeLiteral(label)}-----\r?\n[a-zA-Z0-9+/\n\r=]*?\r?\n-----END ${escapeLiteral(label)}-----`,
)