Compare commits
14 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 5b08eecca9 | |||
| 7c0cb5eabd | |||
| 3f0d6fb438 | |||
| 6cd3a5bc58 | |||
| 88cf5cf281 | |||
| 526545ee68 | |||
| ecf064b9ae | |||
| fd71485960 | |||
| 1c29ba7206 | |||
| 17e9ef2783 | |||
| 983f2a71f0 | |||
| edf60b3e3d | |||
| 1f10d0db14 | |||
| 4bf1768529 |
@@ -26,6 +26,7 @@ ops/proxy.service
|
|||||||
# at /docs (routes/docs.js) so they're readable without internet access.
|
# at /docs (routes/docs.js) so they're readable without internet access.
|
||||||
*.md
|
*.md
|
||||||
!README.md
|
!README.md
|
||||||
|
!CHANGELOG.md
|
||||||
!DEPLOYMENT.md
|
!DEPLOYMENT.md
|
||||||
!nodejs/api.md
|
!nodejs/api.md
|
||||||
!docs/**/*.md
|
!docs/**/*.md
|
||||||
|
|||||||
@@ -0,0 +1,53 @@
|
|||||||
|
# Changelog
|
||||||
|
|
||||||
|
All notable changes to this project are documented here. Format loosely
|
||||||
|
follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/); versions
|
||||||
|
correspond to git tags (`vX.Y.Z`) and `nodejs/package.json`'s `version`.
|
||||||
|
|
||||||
|
## [Unreleased]
|
||||||
|
|
||||||
|
## [1.1.5] - 2026-07-16
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
- Bumped `jq-repeat` 2.0.1 -> 2.1.0. Fixed real breakage: `users.ejs`/`groups.ejs`/`permissions.ejs` called the removed `$.scope.X.__setPut(fn)`/`__setTake(fn)` setter-method API; insert/remove row hooks are now set via direct property assignment (`$.scope.X.__put = fn`), matching 2.1.0's API.
|
||||||
|
|
||||||
|
## [1.1.4] - 2026-07-16
|
||||||
|
|
||||||
|
### Added
|
||||||
|
- **White-label**: `<title>`, the navbar brand text, and the nav logo image were hardcoded "Proxy - Theta 42"/"Dynamic Proxy". Now driven by new `conf.name`/`conf.logo` keys (defaults unchanged). Footer attribution (copyright, `theta42.com` link, GitHub/license links) and favicon are left as-is. Closes [#45](https://github.com/theta42/proxy/issues/45).
|
||||||
|
|
||||||
|
## [1.1.3] - 2026-07-16
|
||||||
|
|
||||||
|
### Added
|
||||||
|
- `CHANGELOG.md` (this file), backfilled from the release notes for every tag so far and served in-app at `/docs/changelog`. Closes [theta-env#43](https://github.com/theta42/theta-env/issues/43).
|
||||||
|
|
||||||
|
## [1.1.2] - 2026-07-16
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
- **Air-gap**: `DynamicRecord.refreshAll()` called the public-IP resolvers (`api.ipify.org`, `icanhazip.com`, `ifconfig.me`) every 4h on a timer regardless of whether any dynamic DNS records were configured — the one background network call in the repo not actually gated by feature use. Now skips the lookup entirely when there's nothing to refresh.
|
||||||
|
- Removed the stray, unauthenticated `GET /test` page (a leftover jq-repeat demo) that loaded jQuery + Mustache from external CDNs.
|
||||||
|
- Removed a dead IE<9-only `html5shim` script tag pointing at a domain that no longer resolves.
|
||||||
|
|
||||||
|
### Added
|
||||||
|
- **In-app documentation**: `GET /docs` and `GET /docs/:slug` render this project's own README, DEPLOYMENT, `api.md`, and `docs/*.md` server-side — readable from the running app with no dependency on GitHub Pages, which requires internet access to view. Public, no auth, rate-limited.
|
||||||
|
|
||||||
|
## [1.1.1] - 2026-07-16
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
- **DuckDNS provider**: adding a DuckDNS provider no longer pushes this host's public IP to the domain's live A/AAAA record as a side effect of token validation. Validation now writes a fixed marker to the TXT record instead, leaving routing untouched. ([#142](https://github.com/theta42/proxy/pull/142))
|
||||||
|
|
||||||
|
## [1.1.0] - 2026-07-16
|
||||||
|
|
||||||
|
First tagged release. Establishes the `vX.Y.Z` tag convention that the in-app update-check banner polls against going forward.
|
||||||
|
|
||||||
|
### Added
|
||||||
|
- Standalone backup script (`ops/backup.sh`) for deployments not using theta-env's orchestrator — snapshots Redis and `./config`, with retention.
|
||||||
|
- Admin-only in-app banner that checks GitHub releases every 24h and surfaces available updates.
|
||||||
|
|
||||||
|
[Unreleased]: https://github.com/theta42/proxy/compare/v1.1.5...HEAD
|
||||||
|
[1.1.5]: https://github.com/theta42/proxy/compare/v1.1.4...v1.1.5
|
||||||
|
[1.1.4]: https://github.com/theta42/proxy/compare/v1.1.3...v1.1.4
|
||||||
|
[1.1.3]: https://github.com/theta42/proxy/compare/v1.1.2...v1.1.3
|
||||||
|
[1.1.2]: https://github.com/theta42/proxy/compare/v1.1.1...v1.1.2
|
||||||
|
[1.1.1]: https://github.com/theta42/proxy/compare/v1.1.0...v1.1.1
|
||||||
|
[1.1.0]: https://github.com/theta42/proxy/releases/tag/v1.1.0
|
||||||
@@ -113,6 +113,7 @@ COPY nodejs/api.md ./api.md
|
|||||||
# flattened /app (mirrors sso-manager-node's tos.md -> /tos.md convention);
|
# flattened /app (mirrors sso-manager-node's tos.md -> /tos.md convention);
|
||||||
# docs/ mirrors the repo's own top-level docs/ folder.
|
# docs/ mirrors the repo's own top-level docs/ folder.
|
||||||
COPY README.md /README.md
|
COPY README.md /README.md
|
||||||
|
COPY CHANGELOG.md /CHANGELOG.md
|
||||||
COPY DEPLOYMENT.md /DEPLOYMENT.md
|
COPY DEPLOYMENT.md /DEPLOYMENT.md
|
||||||
COPY docs /docs
|
COPY docs /docs
|
||||||
|
|
||||||
|
|||||||
@@ -17,6 +17,8 @@ proxy serves them over TLS with auto-renewing certs and no downtime on changes.
|
|||||||
> the proxy and the SSO find each other without manual config.
|
> the proxy and the SSO find each other without manual config.
|
||||||
|
|
||||||
**Documentation:** [https://theta42.github.io/proxy/](https://theta42.github.io/proxy/)
|
**Documentation:** [https://theta42.github.io/proxy/](https://theta42.github.io/proxy/)
|
||||||
|
([CHANGELOG.md](CHANGELOG.md) for what changed in each release) — also
|
||||||
|
readable from the running app itself at `/docs`, no internet access required.
|
||||||
|
|
||||||
## Screenshots
|
## Screenshots
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,8 @@
|
|||||||
// Using https://github.com/simpleworkjs/conf to handle configuration
|
// Using https://github.com/simpleworkjs/conf to handle configuration
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
|
name: "Dynamic Proxy", // displayed in the UI
|
||||||
|
logo: "/static/img/theta42.svg", // shown in the nav; point at your own file under public/ (or an absolute URL) to white-label
|
||||||
userModel: 'redis', // pam, redis, ldap
|
userModel: 'redis', // pam, redis, ldap
|
||||||
ldap: {
|
ldap: {
|
||||||
url: 'ldap://192.168.1.55:389',
|
url: 'ldap://192.168.1.55:389',
|
||||||
|
|||||||
Generated
+6
-6
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "proxy-api",
|
"name": "proxy-api",
|
||||||
"version": "1.1.2",
|
"version": "1.1.5",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "proxy-api",
|
"name": "proxy-api",
|
||||||
"version": "1.1.2",
|
"version": "1.1.5",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@fortawesome/fontawesome-free": "^7.3.0",
|
"@fortawesome/fontawesome-free": "^7.3.0",
|
||||||
@@ -21,7 +21,7 @@
|
|||||||
"express": "^5.2.1",
|
"express": "^5.2.1",
|
||||||
"express-rate-limit": "^8.5.2",
|
"express-rate-limit": "^8.5.2",
|
||||||
"extend": "^3.0.2",
|
"extend": "^3.0.2",
|
||||||
"jq-repeat": "^2.0.1",
|
"jq-repeat": "^2.1.0",
|
||||||
"jquery": "^4.0.0",
|
"jquery": "^4.0.0",
|
||||||
"ldapts": "^8.1.8",
|
"ldapts": "^8.1.8",
|
||||||
"linux-sys-user": "^1.2.0",
|
"linux-sys-user": "^1.2.0",
|
||||||
@@ -1375,9 +1375,9 @@
|
|||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/jq-repeat": {
|
"node_modules/jq-repeat": {
|
||||||
"version": "2.0.1",
|
"version": "2.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/jq-repeat/-/jq-repeat-2.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/jq-repeat/-/jq-repeat-2.1.0.tgz",
|
||||||
"integrity": "sha512-ATI25tKQG3uHW8f8XPqBe85JsH4PNGHA/YLy1KgMVeYDoUSf9cqGNBum+4A+Pg1WKh9PA6bYyWfYNsgktwIbSg==",
|
"integrity": "sha512-e1OmSWeBEHEtyOhNVysx0bnT5wd6HlZ37JZgPcGPmACJ0K9bXDPq0xOwrM1slQMSTw7FOSNDX+MD6VwvPeeZyQ==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=14.0.0"
|
"node": ">=14.0.0"
|
||||||
|
|||||||
+2
-2
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "proxy-api",
|
"name": "proxy-api",
|
||||||
"version": "1.1.2",
|
"version": "1.1.5",
|
||||||
"private": true,
|
"private": true,
|
||||||
"author": [
|
"author": [
|
||||||
{
|
{
|
||||||
@@ -32,7 +32,7 @@
|
|||||||
"express": "^5.2.1",
|
"express": "^5.2.1",
|
||||||
"express-rate-limit": "^8.5.2",
|
"express-rate-limit": "^8.5.2",
|
||||||
"extend": "^3.0.2",
|
"extend": "^3.0.2",
|
||||||
"jq-repeat": "^2.0.1",
|
"jq-repeat": "^2.1.0",
|
||||||
"jquery": "^4.0.0",
|
"jquery": "^4.0.0",
|
||||||
"ldapts": "^8.1.8",
|
"ldapts": "^8.1.8",
|
||||||
"linux-sys-user": "^1.2.0",
|
"linux-sys-user": "^1.2.0",
|
||||||
|
|||||||
@@ -22,6 +22,8 @@ const docsLimiter = rateLimit({
|
|||||||
const values = {
|
const values = {
|
||||||
title: conf.environment !== 'production' ? `dev` : '',
|
title: conf.environment !== 'production' ? `dev` : '',
|
||||||
titleIcon: conf.environment !== 'production' ? `<i class="fa-brands fa-dev"></i>` : '',
|
titleIcon: conf.environment !== 'production' ? `<i class="fa-brands fa-dev"></i>` : '',
|
||||||
|
name: conf.name,
|
||||||
|
logo: conf.logo,
|
||||||
...buildInfo,
|
...buildInfo,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -32,6 +34,7 @@ const values = {
|
|||||||
// there's no way to make this read outside the doc set below.
|
// there's no way to make this read outside the doc set below.
|
||||||
const DOCS = {
|
const DOCS = {
|
||||||
overview: {title: 'Overview', file: path.join(__dirname, '../../README.md')},
|
overview: {title: 'Overview', file: path.join(__dirname, '../../README.md')},
|
||||||
|
changelog: {title: 'Changelog', file: path.join(__dirname, '../../CHANGELOG.md')},
|
||||||
deployment: {title: 'Deployment', file: path.join(__dirname, '../../DEPLOYMENT.md')},
|
deployment: {title: 'Deployment', file: path.join(__dirname, '../../DEPLOYMENT.md')},
|
||||||
api: {title: 'API Reference', file: path.join(__dirname, '../api.md')},
|
api: {title: 'API Reference', file: path.join(__dirname, '../api.md')},
|
||||||
installation: {title: 'Installation', file: path.join(__dirname, '../../docs/installation.md')},
|
installation: {title: 'Installation', file: path.join(__dirname, '../../docs/installation.md')},
|
||||||
|
|||||||
@@ -9,6 +9,8 @@ const buildInfo = require('../utils/build_info');
|
|||||||
const values ={
|
const values ={
|
||||||
title: conf.environment !== 'production' ? `dev` : '',
|
title: conf.environment !== 'production' ? `dev` : '',
|
||||||
titleIcon: conf.environment !== 'production' ? `<i class="fa-brands fa-dev"></i>` : '',
|
titleIcon: conf.environment !== 'production' ? `<i class="fa-brands fa-dev"></i>` : '',
|
||||||
|
name: conf.name,
|
||||||
|
logo: conf.logo,
|
||||||
...buildInfo,
|
...buildInfo,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -60,10 +60,10 @@
|
|||||||
|
|
||||||
loadUserSuggestions();
|
loadUserSuggestions();
|
||||||
|
|
||||||
$.scope.LocalGroup.__setTake(function($el){
|
$.scope.LocalGroup.__take = function($el){
|
||||||
$el.addClass('bg-danger');
|
$el.addClass('bg-danger');
|
||||||
$el.fadeOut(600, function(){ $el.remove(); });
|
$el.fadeOut(600, function(){ $el.remove(); });
|
||||||
});
|
};
|
||||||
|
|
||||||
app.subscribe(/^model:LocalGroup:create/, function(data){
|
app.subscribe(/^model:LocalGroup:create/, function(data){
|
||||||
$.scope.LocalGroup.remove(data.name);
|
$.scope.LocalGroup.remove(data.name);
|
||||||
|
|||||||
@@ -57,10 +57,10 @@
|
|||||||
|
|
||||||
loadSubjectSuggestions();
|
loadSubjectSuggestions();
|
||||||
|
|
||||||
$.scope.Permission.__setTake(function($el, item, list){
|
$.scope.Permission.__take = function($el, item, list){
|
||||||
$el.addClass('bg-danger');
|
$el.addClass('bg-danger');
|
||||||
$el.fadeOut(600, function(){ $el.remove(); });
|
$el.fadeOut(600, function(){ $el.remove(); });
|
||||||
});
|
};
|
||||||
|
|
||||||
// Live updates (model:Permission:*), so adds/removes reflect for everyone.
|
// Live updates (model:Permission:*), so adds/removes reflect for everyone.
|
||||||
app.subscribe(/^model:Permission:create/, function(data){
|
app.subscribe(/^model:Permission:create/, function(data){
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
||||||
<title>Proxy - Theta 42 <%- title %></title>
|
<title><%- name %> <%- title %></title>
|
||||||
<!-- Favicon -->
|
<!-- Favicon -->
|
||||||
<link rel="icon" type="image/svg+xml" href="/static/favicon.svg">
|
<link rel="icon" type="image/svg+xml" href="/static/favicon.svg">
|
||||||
<!-- CSS are placed here -->
|
<!-- CSS are placed here -->
|
||||||
@@ -28,7 +28,7 @@
|
|||||||
<body>
|
<body>
|
||||||
|
|
||||||
<nav class="navbar navbar-expand-md navbar-dark fixed-top bg-dark">
|
<nav class="navbar navbar-expand-md navbar-dark fixed-top bg-dark">
|
||||||
<a class="navbar-brand" href="#">Dynamic Proxy <%- titleIcon %></a>
|
<a class="navbar-brand" href="#"><img src="<%- logo %>" height="28" class="me-2" alt=""><%- name %> <%- titleIcon %></a>
|
||||||
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
|
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
|
||||||
<span class="navbar-toggler-icon"></span>
|
<span class="navbar-toggler-icon"></span>
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
@@ -26,12 +26,12 @@
|
|||||||
for(let user of data.results){
|
for(let user of data.results){
|
||||||
$.scope.users.push(user);
|
$.scope.users.push(user);
|
||||||
}
|
}
|
||||||
$.scope.users.__setPut(function($el, item, list){
|
$.scope.users.__put = function($el, item, list){
|
||||||
$el.addClass('bg-success');
|
$el.addClass('bg-success');
|
||||||
$el.fadeIn(3000, function(){
|
$el.fadeIn(3000, function(){
|
||||||
$el.removeClass('bg-success');
|
$el.removeClass('bg-success');
|
||||||
});
|
});
|
||||||
})
|
};
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -45,12 +45,12 @@
|
|||||||
$(document).ready(function(){
|
$(document).ready(function(){
|
||||||
populateUsers(); //populate the table
|
populateUsers(); //populate the table
|
||||||
|
|
||||||
$.scope.users.__setTake(function($el, item, list){
|
$.scope.users.__take = function($el, item, list){
|
||||||
$el.addClass('bg-danger');
|
$el.addClass('bg-danger');
|
||||||
$el.fadeOut(1000, function(){
|
$el.fadeOut(1000, function(){
|
||||||
$el.remove()
|
$el.remove()
|
||||||
});
|
});
|
||||||
});
|
};
|
||||||
|
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -18,6 +18,9 @@
|
|||||||
// theta-env orchestrator (setup.sh) and ignored by the app.
|
// theta-env orchestrator (setup.sh) and ignored by the app.
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
|
name: 'Dynamic Proxy', // shown in the UI
|
||||||
|
logo: '/static/img/theta42.svg', // nav image; point at your own file under public/ to white-label
|
||||||
|
|
||||||
// OpenID Connect — point at your SSO Manager. Issuer + authorization/
|
// OpenID Connect — point at your SSO Manager. Issuer + authorization/
|
||||||
// endSession are browser-facing URLs; token/userinfo can be the internal
|
// endSession are browser-facing URLs; token/userinfo can be the internal
|
||||||
// URL if the SSO is on the same docker network (avoids a TLS hairpin).
|
// URL if the SSO is on the same docker network (avoids a TLS hairpin).
|
||||||
|
|||||||
Reference in New Issue
Block a user