Skip to content
Merged

1.6.0 #102

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions .github/ISSUE_TEMPLATE/1.bug_report.yml
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,14 @@ body:
validations:
required: true

- type: textarea
attributes:
label: AI Disclosure
description: |
If you used AI to help write this issue, please disclose it here. This is important for transparency and helps maintain the integrity of the issue tracking process.
validations:
required: true

- type: textarea
attributes:
label: Expected Behavior
Expand Down
4 changes: 4 additions & 0 deletions .github/PULL_REQUEST_TEMPLATE.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@ perpetual license to use, modify, and redistribute these contributions under any
choose, including both the AGPLv3 and the Fossorial Commercial license terms. I
represent that I have the right to grant this license for all contributed content.

## AI Disclosure

> Please disclose how AI was used in this pull request. The use of AI does not preclude this from being merged but is an important factor in how we review your request.

## Description


Expand Down
25 changes: 13 additions & 12 deletions .github/workflows/linting.yml
Original file line number Diff line number Diff line change
@@ -1,17 +1,18 @@
name: ESLint

on:
pull_request:
paths:
- '**/*.js'
- '**/*.jsx'
- '**/*.ts'
- '**/*.tsx'
- '.eslintrc*'
- 'package.json'
- 'yarn.lock'
- 'pnpm-lock.yaml'
- 'package-lock.json'
# disable until eslint is updated
# on:
# pull_request:
# paths:
# - '**/*.js'
# - '**/*.jsx'
# - '**/*.ts'
# - '**/*.tsx'
# - '.eslintrc*'
# - 'package.json'
# - 'yarn.lock'
# - 'pnpm-lock.yaml'
# - 'package-lock.json'

jobs:
Linter:
Expand Down
8 changes: 6 additions & 2 deletions install/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package main
import (
"bufio"
"embed"
"flag"
"fmt"
"io"
"io/fs"
Expand Down Expand Up @@ -50,6 +51,9 @@ const (

func main() {

panelURL := flag.String("pangolin-endpoint", DefaultPanelURL, "Base URL of the Pangolin server to fetch node credentials from. Only needed if you are not using Pangolin Cloud (app.pangolin.net).")
flag.Parse()

// print a banner about prerequisites - opening port 80, 443, 51820, and 21820 on the VPS and firewall and pointing your domain to the VPS IP with a records. Docs are at http://localhost:3000/Getting%20Started/dns-networking

fmt.Println("Welcome to the Pangolin Remote Node installer!")
Expand Down Expand Up @@ -85,7 +89,7 @@ func main() {
// If the secret and id are not generated then generate them
if config.HybridId == "" || config.HybridSecret == "" {
// fmt.Println("Requesting hybrid credentials from cloud...")
credentials, err := requestHybridCredentials()
credentials, err := requestHybridCredentials(*panelURL)
if err != nil {
fmt.Printf("Error requesting hybrid credentials: %v\n", err)
fmt.Println("Please obtain credentials manually from the dashboard and run the installer again.")
Expand All @@ -96,7 +100,7 @@ func main() {
fmt.Printf("Your managed credentials have been obtained successfully.\n")
fmt.Printf(" ID: %s\n", config.HybridId)
fmt.Printf(" Secret: %s\n", config.HybridSecret)
fmt.Print("\nTake these to the Pangolin dashboard https://app.pangolin.net to adopt your node.\n\n")
fmt.Printf("\nTake these to the Pangolin dashboard %s to adopt your node.\n\n", *panelURL)
readBool(reader, "Have you adopted your node?", true)
}

Expand Down
16 changes: 10 additions & 6 deletions install/quickStart.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,15 @@ import (
"fmt"
"io"
"net/http"
"strings"
"time"
)

const (
FRONTEND_SECRET_KEY = "af4e4785-7e09-11f0-b93a-74563c4e2a7e"
// CLOUD_API_URL = "https://pangolin.fossorial.io/api/v1/remote-exit-node/quick-start"
CLOUD_API_URL = "https://app.pangolin.net/api/v1/remote-exit-node/quick-start"

// DefaultPanelURL is Pangolin Cloud, used unless the --pangolin-endpoint flag points elsewhere.
DefaultPanelURL = "https://app.pangolin.net"
)

// HybridCredentials represents the response from the cloud API
Expand Down Expand Up @@ -42,9 +44,10 @@ func generateValidationToken() string {
return base64.StdEncoding.EncodeToString(obfuscated)
}

// requestHybridCredentials makes an HTTP POST request to the cloud API
// to get hybrid credentials (ID and secret)
func requestHybridCredentials() (*HybridCredentials, error) {
// requestHybridCredentials makes an HTTP POST request to the panel API
// to get hybrid credentials (ID and secret). panelURL is the base URL of
// the Pangolin panel (Pangolin Cloud by default, or a self-hosted instance).
func requestHybridCredentials(panelURL string) (*HybridCredentials, error) {
// Generate validation token
token := generateValidationToken()

Expand All @@ -60,7 +63,8 @@ func requestHybridCredentials() (*HybridCredentials, error) {
}

// Create HTTP request
req, err := http.NewRequest("POST", CLOUD_API_URL, bytes.NewBuffer(jsonData))
apiURL := strings.TrimRight(panelURL, "/") + "/api/v1/remote-exit-node/quick-start"
req, err := http.NewRequest("POST", apiURL, bytes.NewBuffer(jsonData))
if err != nil {
return nil, fmt.Errorf("failed to create HTTP request: %v", err)
}
Expand Down
57 changes: 17 additions & 40 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 2 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,9 @@
"express-rate-limit": "8.7.1",
"helmet": "8.3.0",
"http-errors": "2.0.1",
"lru-cache": "11.5.2",
"js-yaml": "5.4.3",
"node-cache": "5.1.2",
"pg": "8.23.1",
"react": "19.3.0",
"semver": "7.8.5",
"winston": "3.19.0",
"winston-daily-rotate-file": "5.0.0",
Expand All @@ -51,7 +50,6 @@
"esbuild": "0.28.2",
"esbuild-node-externals": "2.0.0",
"tsx": "4.23.15",
"typescript": "7.0.2",
"typescript-eslint": "8.71.1"
"typescript": "7.0.2"
}
}
2 changes: 2 additions & 0 deletions server/auth/sessions/resource.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,4 +78,6 @@ export async function createAccessTokenResourceSession(

export type ResourceSessionValidationResult = {
resourceSession: ResourceSession | null;
// Email of the whitelist entry the session was issued for, if any
whitelistEmail?: string | null;
};
2 changes: 1 addition & 1 deletion server/lib/consts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import path from "path";
import { fileURLToPath } from "url";

// This is a placeholder value replaced by the build process
export const APP_VERSION = "1.1.2";
export const APP_VERSION = "1.6.0";

export const __FILENAME = fileURLToPath(import.meta.url);
export const __DIRNAME = path.dirname(__FILENAME);
Expand Down
79 changes: 79 additions & 0 deletions server/lib/createLocalCache.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
import { LRUCache } from "lru-cache";

const DEFAULT_MAX_KEYS = 10000;
const DEFAULT_TTL_MS = 3600 * 1000;

export type LocalCache = {
get<T>(key: string): T | undefined;
set(key: string, value: unknown, ttlSeconds?: number): boolean;
del(key: string | string[]): number;
has(key: string): boolean;
keys(): string[];
flushAll(): void;
getStats(): { keys: number };
getTtl(key: string): number | undefined;
};

export function createLocalCache(max = DEFAULT_MAX_KEYS): LocalCache {
const lru = new LRUCache<string, {}>({
max,
ttl: DEFAULT_TTL_MS,
updateAgeOnGet: false
});

return {
get<T>(key: string): T | undefined {
return lru.get(key) as T | undefined;
},

set(key: string, value: unknown, ttlSeconds?: number): boolean {
const stored = value as {};
if (ttlSeconds === undefined) {
lru.set(key, stored);
} else if (ttlSeconds === 0) {
lru.set(key, stored, { ttl: 0 });
} else {
lru.set(key, stored, { ttl: ttlSeconds * 1000 });
}
return true;
},

del(key: string | string[]): number {
const keys = Array.isArray(key) ? key : [key];
let deleted = 0;
for (const k of keys) {
if (lru.delete(k)) {
deleted++;
}
}
return deleted;
},

has(key: string): boolean {
return lru.has(key);
},

keys(): string[] {
return [...lru.keys()];
},

flushAll(): void {
lru.clear();
},

getStats(): { keys: number } {
return { keys: lru.size };
},

getTtl(key: string): number | undefined {
if (!lru.has(key)) {
return undefined;
}
const remaining = lru.getRemainingTTL(key);
if (!Number.isFinite(remaining)) {
return 0;
}
return Date.now() + remaining;
}
};
}
Loading
Loading