Compare commits
17 Commits
9f28113573
...
v1.9.7
| Author | SHA1 | Date | |
|---|---|---|---|
| d1f0e38374 | |||
| 84d024f4c6 | |||
| 1fa28590b4 | |||
| 30a3ea66d5 | |||
| 6f2cfad65f | |||
| 2621d233f9 | |||
| af4997df5a | |||
| 1980fd04ba | |||
| d06d0c76c2 | |||
| 9244328da8 | |||
| 70489c3dac | |||
| 2a3ee1c8af | |||
| 8a01cb4755 | |||
| d85b409662 | |||
| 9dfea6c795 | |||
| 29cb46963c | |||
| ca7c5f77a4 |
+2
-1
@@ -4,7 +4,8 @@ CHANGELOG.md
|
|||||||
*.md
|
*.md
|
||||||
|
|
||||||
# Deployment files
|
# Deployment files
|
||||||
deployment.yml
|
deployment-dev.yml
|
||||||
|
deployment-prod.yml
|
||||||
run.sh
|
run.sh
|
||||||
Dockerfile
|
Dockerfile
|
||||||
.dockerignore
|
.dockerignore
|
||||||
|
|||||||
@@ -0,0 +1,18 @@
|
|||||||
|
name: Dev
|
||||||
|
|
||||||
|
on:
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
runs-on: build-htz-01
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
|
||||||
|
- name: Build and push Docker image
|
||||||
|
run: |
|
||||||
|
docker build -t cr.jdbnet.co.uk/public/ipam:dev --build-arg VERSION=dev .
|
||||||
|
docker push cr.jdbnet.co.uk/public/ipam:dev
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
name: Release
|
||||||
|
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
types: [closed]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
release:
|
||||||
|
if: github.event.pull_request.merged == true && startsWith(github.head_ref, 'v')
|
||||||
|
runs-on: build-htz-01
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
|
||||||
|
- name: Extract Version
|
||||||
|
id: get_version
|
||||||
|
run: echo "VERSION=${{ github.head_ref }}" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
|
- name: Generate Changelog
|
||||||
|
id: changelog
|
||||||
|
uses: https://github.com/metcalfc/changelog-generator@v4.6.2
|
||||||
|
with:
|
||||||
|
myToken: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
|
- name: Build and push Docker image
|
||||||
|
run: |
|
||||||
|
VERSION=${{ steps.get_version.outputs.VERSION }}
|
||||||
|
docker build -t cr.jdbnet.co.uk/public/ipam:$VERSION \
|
||||||
|
-t cr.jdbnet.co.uk/public/ipam:latest \
|
||||||
|
--build-arg VERSION=$VERSION \
|
||||||
|
.
|
||||||
|
docker push cr.jdbnet.co.uk/public/ipam:$VERSION
|
||||||
|
docker push cr.jdbnet.co.uk/public/ipam:latest
|
||||||
|
|
||||||
|
- name: Create Gitea Release
|
||||||
|
uses: https://gitea.com/actions/gitea-release-action@v1
|
||||||
|
with:
|
||||||
|
tag_name: ${{ steps.get_version.outputs.VERSION }}
|
||||||
|
name: ${{ steps.get_version.outputs.VERSION }}
|
||||||
|
body: ${{ steps.changelog.outputs.changelog }}
|
||||||
|
draft: false
|
||||||
|
prerelease: false
|
||||||
@@ -1,59 +0,0 @@
|
|||||||
name: Release Please
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches:
|
|
||||||
- main
|
|
||||||
|
|
||||||
permissions:
|
|
||||||
contents: write
|
|
||||||
pull-requests: write
|
|
||||||
packages: write
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
release-please:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
outputs:
|
|
||||||
release_created: ${{ steps.release.outputs.release_created }}
|
|
||||||
steps:
|
|
||||||
- uses: googleapis/release-please-action@v4
|
|
||||||
id: release
|
|
||||||
with:
|
|
||||||
manifest-file: .release-please-manifest.json
|
|
||||||
config-file: .release-please-config.json
|
|
||||||
|
|
||||||
- name: Checkout
|
|
||||||
if: ${{ steps.release.outputs.release_created }}
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
|
|
||||||
- name: Set up Docker Buildx
|
|
||||||
if: ${{ steps.release.outputs.release_created }}
|
|
||||||
uses: docker/setup-buildx-action@v3
|
|
||||||
|
|
||||||
- name: Log in to GitHub Container Registry
|
|
||||||
if: ${{ steps.release.outputs.release_created }}
|
|
||||||
uses: docker/login-action@v3
|
|
||||||
with:
|
|
||||||
registry: ghcr.io
|
|
||||||
username: ${{ github.actor }}
|
|
||||||
password: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
|
|
||||||
- name: Read version
|
|
||||||
if: ${{ steps.release.outputs.release_created }}
|
|
||||||
id: version
|
|
||||||
run: |
|
|
||||||
VERSION=$(cat VERSION)
|
|
||||||
echo "version=$VERSION" >> $GITHUB_OUTPUT
|
|
||||||
echo "VERSION=$VERSION" >> $GITHUB_ENV
|
|
||||||
|
|
||||||
- name: Build and push Docker image
|
|
||||||
if: ${{ steps.release.outputs.release_created }}
|
|
||||||
uses: docker/build-push-action@v6
|
|
||||||
with:
|
|
||||||
context: .
|
|
||||||
push: true
|
|
||||||
tags: |
|
|
||||||
ghcr.io/jdb-net/ipam:${{ env.VERSION }}
|
|
||||||
ghcr.io/jdb-net/ipam:latest
|
|
||||||
build-args: |
|
|
||||||
VERSION=${{ env.VERSION }}
|
|
||||||
@@ -1,46 +0,0 @@
|
|||||||
name: Staging
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches: [ main ]
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
build-and-push:
|
|
||||||
name: Build and Push
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
permissions:
|
|
||||||
contents: read
|
|
||||||
packages: write
|
|
||||||
steps:
|
|
||||||
- name: Checkout
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
|
|
||||||
- name: Set up Docker Buildx
|
|
||||||
uses: docker/setup-buildx-action@v3
|
|
||||||
|
|
||||||
- name: Log in to GitHub Container Registry
|
|
||||||
uses: docker/login-action@v3
|
|
||||||
with:
|
|
||||||
registry: ghcr.io
|
|
||||||
username: ${{ github.actor }}
|
|
||||||
password: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
|
|
||||||
- name: Build and push
|
|
||||||
uses: docker/build-push-action@v6
|
|
||||||
with:
|
|
||||||
context: .
|
|
||||||
push: true
|
|
||||||
tags: |
|
|
||||||
ghcr.io/jdb-net/ipam:staging
|
|
||||||
|
|
||||||
deploy:
|
|
||||||
name: Deploy to Kubernetes
|
|
||||||
needs: build-and-push
|
|
||||||
runs-on: [ k3s-internal-htz-01 ]
|
|
||||||
steps:
|
|
||||||
- name: Checkout
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
|
|
||||||
- name: Apply manifests
|
|
||||||
run: |
|
|
||||||
sudo kubectl replace -f deployment.yml --grace-period=60 --force
|
|
||||||
@@ -1,54 +0,0 @@
|
|||||||
{
|
|
||||||
"packages": {
|
|
||||||
".": {
|
|
||||||
"release-type": "simple",
|
|
||||||
"version-file": "VERSION",
|
|
||||||
"extra-files": [
|
|
||||||
"CHANGELOG.md"
|
|
||||||
],
|
|
||||||
"changelog-sections": [
|
|
||||||
{
|
|
||||||
"type": "feat",
|
|
||||||
"section": "Features"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "fix",
|
|
||||||
"section": "Bug Fixes"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "refactor",
|
|
||||||
"section": "Refactoring"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "style",
|
|
||||||
"section": "Style Changes"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "perf",
|
|
||||||
"section": "Performance Improvements"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "docs",
|
|
||||||
"section": "Documentation"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "test",
|
|
||||||
"section": "Tests"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "build",
|
|
||||||
"section": "Build System"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "ci",
|
|
||||||
"section": "CI/CD"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "chore",
|
|
||||||
"section": "Chores"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"$schema": "https://raw.githubusercontent.com/googleapis/release-please/main/schemas/config.json"
|
|
||||||
}
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
{
|
|
||||||
".": "1.10.0"
|
|
||||||
}
|
|
||||||
-186
@@ -1,186 +0,0 @@
|
|||||||
# Changelog
|
|
||||||
|
|
||||||
## [1.10.0](https://github.com/JDB-NET/ipam/compare/v1.9.1...v1.10.0) (2025-12-31)
|
|
||||||
|
|
||||||
|
|
||||||
### Features
|
|
||||||
|
|
||||||
* :sparkles: feature flags ([c1b0a70](https://github.com/JDB-NET/ipam/commit/c1b0a7084b7de1c476d588b9b71db0c267051c59))
|
|
||||||
|
|
||||||
## [1.9.1](https://github.com/JDB-NET/ipam/compare/v1.9.0...v1.9.1) (2025-12-29)
|
|
||||||
|
|
||||||
|
|
||||||
### Bug Fixes
|
|
||||||
|
|
||||||
* :bug: device page dictionary ([83c1b21](https://github.com/JDB-NET/ipam/commit/83c1b21c04163e22c25831ee8064f3cd5ea2c99d))
|
|
||||||
|
|
||||||
## [1.9.0](https://github.com/JDB-NET/ipam/compare/v1.8.0...v1.9.0) (2025-12-27)
|
|
||||||
|
|
||||||
|
|
||||||
### Features
|
|
||||||
|
|
||||||
* :sparkles: api rate limiting ([e316a16](https://github.com/JDB-NET/ipam/commit/e316a1638661e023a23b4b164fc2c773cd2f7e2a))
|
|
||||||
* :sparkles: custom fields by device or subnet ([b23cda4](https://github.com/JDB-NET/ipam/commit/b23cda48af575b92a16be2c211e2b2ebb9008a56))
|
|
||||||
* :sparkles: ip address history ([21042b7](https://github.com/JDB-NET/ipam/commit/21042b7fd701ecf025ba6d9407137d05156d884a))
|
|
||||||
* :sparkles: ip address notes/descriptions ([8b001a0](https://github.com/JDB-NET/ipam/commit/8b001a047b263501300213d40a474b19e976cabb))
|
|
||||||
* :sparkles: log api usage to audit log ([e028f96](https://github.com/JDB-NET/ipam/commit/e028f9610cb09c5551594a32fada43e7078a2a73))
|
|
||||||
* :sparkles: two factor authentication ([5037c1b](https://github.com/JDB-NET/ipam/commit/5037c1b57823a59ed4dfda6dc3a16d570cde47bd))
|
|
||||||
* :sparkles: vlan management ([c7350ae](https://github.com/JDB-NET/ipam/commit/c7350aeb1f5b3ac471e40a351c66f9a82b70bdf4))
|
|
||||||
|
|
||||||
|
|
||||||
### Bug Fixes
|
|
||||||
|
|
||||||
* :bug: 2fa verification ([53dc19a](https://github.com/JDB-NET/ipam/commit/53dc19a549ca4255d22cc9812c6e7e83e1b76697))
|
|
||||||
|
|
||||||
|
|
||||||
### Refactoring
|
|
||||||
|
|
||||||
* :art: auto save custom fields ([7e1c4b1](https://github.com/JDB-NET/ipam/commit/7e1c4b126e0d45010b17af7be64f31c85e33d76d))
|
|
||||||
* :art: minify ([9106799](https://github.com/JDB-NET/ipam/commit/91067994bac7688b95e8c71a69b14967f640407e))
|
|
||||||
|
|
||||||
|
|
||||||
### Style Changes
|
|
||||||
|
|
||||||
* :lipstick: backup code button ([181e2b2](https://github.com/JDB-NET/ipam/commit/181e2b2ca53b2c7b99899f54bef028a3d9ac30eb))
|
|
||||||
|
|
||||||
## [1.8.0](https://github.com/JDB-NET/ipam/compare/v1.7.0...v1.8.0) (2025-12-23)
|
|
||||||
|
|
||||||
|
|
||||||
### Features
|
|
||||||
|
|
||||||
* :sparkles: get next available ip by api ([64ae4be](https://github.com/JDB-NET/ipam/commit/64ae4be6d5997ff0b16ff5232237d38f2fec5b64))
|
|
||||||
|
|
||||||
|
|
||||||
### Bug Fixes
|
|
||||||
|
|
||||||
* :bug: global search missing from devices ([283c445](https://github.com/JDB-NET/ipam/commit/283c445263b7dc992448d907e682e53b7720b610))
|
|
||||||
|
|
||||||
|
|
||||||
### Build System
|
|
||||||
|
|
||||||
* :rocket: redeploy ([d7fcffd](https://github.com/JDB-NET/ipam/commit/d7fcffd4b5598b682dede864ba526b1257584f6a))
|
|
||||||
|
|
||||||
## [1.7.0](https://github.com/JDB-NET/ipam/compare/v1.6.1...v1.7.0) (2025-12-05)
|
|
||||||
|
|
||||||
|
|
||||||
### Features
|
|
||||||
|
|
||||||
* :sparkles: add devices by tag page ([9c0e6d0](https://github.com/JDB-NET/ipam/commit/9c0e6d035c8dda68281b2bfe2b7a61802353f7a7))
|
|
||||||
|
|
||||||
|
|
||||||
### Bug Fixes
|
|
||||||
|
|
||||||
* :bug: invalidate cache when device type is added ([47208b3](https://github.com/JDB-NET/ipam/commit/47208b31eed51f0cf0d7c8c411093bda1c84cf1b))
|
|
||||||
* :bug: invalidate linked cache ([8242e9d](https://github.com/JDB-NET/ipam/commit/8242e9d758ef19030b516e4a51f0cfb556f4e5ba))
|
|
||||||
|
|
||||||
## [1.6.1](https://github.com/JDB-NET/ipam/compare/v1.6.0...v1.6.1) (2025-12-05)
|
|
||||||
|
|
||||||
|
|
||||||
### Bug Fixes
|
|
||||||
|
|
||||||
* :bug: invalidate subnet cache when device is deleted ([286bf4b](https://github.com/JDB-NET/ipam/commit/286bf4b665e6352dea7b14753f080fa5cabb7926))
|
|
||||||
|
|
||||||
## [1.6.0](https://github.com/JDB-NET/ipam/compare/v1.5.1...v1.6.0) (2025-12-05)
|
|
||||||
|
|
||||||
|
|
||||||
### Features
|
|
||||||
|
|
||||||
* :sparkles: backup and restore ([707846b](https://github.com/JDB-NET/ipam/commit/707846bb3c717df9223ea7103e29efc6e671e16d))
|
|
||||||
* :sparkles: bulk operations ([2163be8](https://github.com/JDB-NET/ipam/commit/2163be8f79b579e38944a689915a18d5c35f8d3a))
|
|
||||||
* :sparkles: global search ([3e8965d](https://github.com/JDB-NET/ipam/commit/3e8965de6f19b3b382e236b08df685401205f356))
|
|
||||||
* :sparkles: in memory cache ([3a9250f](https://github.com/JDB-NET/ipam/commit/3a9250f5b0c14bfc6a807fe2948bbc852a652047))
|
|
||||||
* :sparkles: subnet utilisation stats ([f98e92d](https://github.com/JDB-NET/ipam/commit/f98e92da062640d47bec3516def0efde3aebd058))
|
|
||||||
* :sparkles: update available notification ([730b870](https://github.com/JDB-NET/ipam/commit/730b8701db81f5e03760a25209baeab2f81116fa))
|
|
||||||
|
|
||||||
|
|
||||||
### Refactoring
|
|
||||||
|
|
||||||
* :art: database indexing and optimisation ([47f68fd](https://github.com/JDB-NET/ipam/commit/47f68fd27cf62d0e0d2af55089bc0556043c12ff))
|
|
||||||
* :art: header link to github releases ([61e3200](https://github.com/JDB-NET/ipam/commit/61e320020724e437d8a607e7341b12b2fe6f794d))
|
|
||||||
* :art: improved audit log filtering ([f016598](https://github.com/JDB-NET/ipam/commit/f0165985fc194fd3a3e460b52447a5511908ed91))
|
|
||||||
* :art: js ([1d9209a](https://github.com/JDB-NET/ipam/commit/1d9209a714a6d0b7d1901b6e3470f5265e0171a6))
|
|
||||||
* :art: tidy nav bar ([69588d6](https://github.com/JDB-NET/ipam/commit/69588d6518571d8de55c718c14176bb78cb19ee1))
|
|
||||||
|
|
||||||
|
|
||||||
### CI/CD
|
|
||||||
|
|
||||||
* :rocket: include all commit types ([f6795f5](https://github.com/JDB-NET/ipam/commit/f6795f52815a2d599840c8ed83c99ad690a046c8))
|
|
||||||
|
|
||||||
## [1.5.1](https://github.com/JDB-NET/ipam/compare/v1.5.0...v1.5.1) (2025-12-04)
|
|
||||||
|
|
||||||
|
|
||||||
### Bug Fixes
|
|
||||||
|
|
||||||
* :bug: audit log on mobile ([6f01c99](https://github.com/JDB-NET/ipam/commit/6f01c9956f4a31414a082a779eb493735df0b8e6))
|
|
||||||
|
|
||||||
## [1.5.0](https://github.com/JDB-NET/ipam/compare/v1.4.2...v1.5.0) (2025-11-21)
|
|
||||||
|
|
||||||
|
|
||||||
### Features
|
|
||||||
|
|
||||||
* :sparkles: device tags ([ad1e576](https://github.com/JDB-NET/ipam/commit/ad1e576da42bf90c59347f7f7a4cce13c6842204))
|
|
||||||
|
|
||||||
## [1.4.2](https://github.com/JDB-NET/ipam/compare/v1.4.1...v1.4.2) (2025-11-08)
|
|
||||||
|
|
||||||
|
|
||||||
### Bug Fixes
|
|
||||||
|
|
||||||
* :bug: ensure all fields are updated by api ([5c1ad03](https://github.com/JDB-NET/ipam/commit/5c1ad039904b2c8c8629242b5558b03da5ad782c))
|
|
||||||
|
|
||||||
## [1.4.1](https://github.com/JDB-NET/ipam/compare/v1.4.0...v1.4.1) (2025-11-06)
|
|
||||||
|
|
||||||
|
|
||||||
### Bug Fixes
|
|
||||||
|
|
||||||
* :bug: pagination no longer gets out of control ([80b6de3](https://github.com/JDB-NET/ipam/commit/80b6de395fc4ddb4e7cd3ece89b423af2667d298))
|
|
||||||
* :bug: styling of admin and users pages ([d56e064](https://github.com/JDB-NET/ipam/commit/d56e0647f74fba1db1f504e02364406691ede9f3))
|
|
||||||
|
|
||||||
## [1.4.0](https://github.com/JDB-NET/ipam/compare/v1.3.0...v1.4.0) (2025-11-06)
|
|
||||||
|
|
||||||
|
|
||||||
### Features
|
|
||||||
|
|
||||||
* :sparkles: full api integration ([c53472c](https://github.com/JDB-NET/ipam/commit/c53472c5d760e28e53a737cb0546e85c9a422d15))
|
|
||||||
|
|
||||||
## [1.3.0](https://github.com/JDB-NET/ipam/compare/v1.2.0...v1.3.0) (2025-11-06)
|
|
||||||
|
|
||||||
|
|
||||||
### Features
|
|
||||||
|
|
||||||
* :sparkles: role based access control ([3bf2697](https://github.com/JDB-NET/ipam/commit/3bf269701030bc1f14a48c5af488286c424dbfa7))
|
|
||||||
|
|
||||||
## [1.2.0](https://github.com/JDB-NET/ipam/compare/v1.1.1...v1.2.0) (2025-11-06)
|
|
||||||
|
|
||||||
|
|
||||||
### Features
|
|
||||||
|
|
||||||
* :sparkles: added the ability to create/edit/remove device types ([d68eefc](https://github.com/JDB-NET/ipam/commit/d68eefcf0cc4a59cda9cedb3e126d974ee45d2ad))
|
|
||||||
|
|
||||||
|
|
||||||
### Bug Fixes
|
|
||||||
|
|
||||||
* :bug: missing button classes ([f93fa15](https://github.com/JDB-NET/ipam/commit/f93fa155eb5d6c9ff4ed19f332c3ad6fff328d31))
|
|
||||||
|
|
||||||
## [1.1.1](https://github.com/JDB-NET/ipam/compare/v1.1.0...v1.1.1) (2025-11-01)
|
|
||||||
|
|
||||||
|
|
||||||
### Bug Fixes
|
|
||||||
|
|
||||||
* :bug: image name ([de123fa](https://github.com/JDB-NET/ipam/commit/de123fafd40d97ea6e545bd8dd1d3a812e2a709f))
|
|
||||||
|
|
||||||
## [1.1.0](https://github.com/JDB-NET/ipam/compare/v1.0.0...v1.1.0) (2025-11-01)
|
|
||||||
|
|
||||||
|
|
||||||
### Features
|
|
||||||
|
|
||||||
* Added icon on login button. Closes [#1](https://github.com/JDB-NET/ipam/issues/1) ([6e068b6](https://github.com/JDB-NET/ipam/commit/6e068b672592f7d23ca66a0a6189b5763d89a698))
|
|
||||||
* Added light mode up to admin ([38c8402](https://github.com/JDB-NET/ipam/commit/38c840251f03c8f1e1a2c407efa77621df70ce2f))
|
|
||||||
* Rack stuff now complete ([5d220d3](https://github.com/JDB-NET/ipam/commit/5d220d354df83db8b2bfbf8e2c87bd78ba91f6e5))
|
|
||||||
|
|
||||||
|
|
||||||
### Bug Fixes
|
|
||||||
|
|
||||||
* Back buttons now hidden on mobile ([40a7a2f](https://github.com/JDB-NET/ipam/commit/40a7a2f2d58f6c89a7e7e74908c088e7eddf966a))
|
|
||||||
* Corrected image in deployment ([9ecd492](https://github.com/JDB-NET/ipam/commit/9ecd492065fcd226d274f8e343d401437e1c8de8))
|
|
||||||
* Fixed back button on device page ([9734e4d](https://github.com/JDB-NET/ipam/commit/9734e4df0b27461867393c132991f9e2ec907de4))
|
|
||||||
* Fixed database initialisation and dropped to 1 worker ([7cd6a0f](https://github.com/JDB-NET/ipam/commit/7cd6a0f96d8dc20743603d55498d8c1af8069690))
|
|
||||||
@@ -1,6 +1,9 @@
|
|||||||
FROM python:3.13-slim
|
FROM python:3.13-slim
|
||||||
|
LABEL org.opencontainers.image.vendor="JDB-NET"
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
COPY . /app
|
COPY . /app
|
||||||
|
ARG VERSION=unknown
|
||||||
|
ENV VERSION=${VERSION}
|
||||||
RUN pip install -r requirements.txt
|
RUN pip install -r requirements.txt
|
||||||
RUN apt-get update && apt-get install -y curl mariadb-client-compat
|
RUN apt-get update && apt-get install -y curl mariadb-client-compat
|
||||||
RUN rm -rf /var/lib/apt/lists/*
|
RUN rm -rf /var/lib/apt/lists/*
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<div align="center">
|
<div align="center">
|
||||||
<img src="https://projects.jdbnet.co.uk/ipam/img/favicon.png" alt="IPAM" width="200" />
|
<img src="https://assets.jdbnet.co.uk/projects/ipam.png" alt="IPAM" width="200" />
|
||||||
|
|
||||||
# IP Address Management
|
# IP Address Management
|
||||||
</div>
|
</div>
|
||||||
@@ -40,7 +40,7 @@ docker run -d \
|
|||||||
-e SECRET_KEY=your_secret_key \
|
-e SECRET_KEY=your_secret_key \
|
||||||
-e NAME="Your Organisation" \
|
-e NAME="Your Organisation" \
|
||||||
-e LOGO_PNG="https://example.com/logo.png" \
|
-e LOGO_PNG="https://example.com/logo.png" \
|
||||||
ghcr.io/jdb-net/ipam:latest
|
cr.jdbnet.co.uk/public/ipam:latest
|
||||||
```
|
```
|
||||||
|
|
||||||
### Docker Compose
|
### Docker Compose
|
||||||
@@ -48,7 +48,7 @@ docker run -d \
|
|||||||
```yaml
|
```yaml
|
||||||
services:
|
services:
|
||||||
ipam:
|
ipam:
|
||||||
image: ghcr.io/jdb-net/ipam:latest
|
image: cr.jdbnet.co.uk/public/ipam:latest
|
||||||
container_name: ipam
|
container_name: ipam
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
ports:
|
ports:
|
||||||
@@ -247,7 +247,7 @@ spec:
|
|||||||
spec:
|
spec:
|
||||||
containers:
|
containers:
|
||||||
- name: ipam
|
- name: ipam
|
||||||
image: ghcr.io/jdb-net/ipam:latest
|
image: cr.jdbnet.co.uk/public/ipam:latest
|
||||||
ports:
|
ports:
|
||||||
- containerPort: 5000
|
- containerPort: 5000
|
||||||
env:
|
env:
|
||||||
|
|||||||
@@ -27,21 +27,14 @@ limiter = Limiter(
|
|||||||
|
|
||||||
@app.context_processor
|
@app.context_processor
|
||||||
def inject_env_vars():
|
def inject_env_vars():
|
||||||
version = 'unknown'
|
version = os.environ.get('VERSION', 'unknown')
|
||||||
try:
|
|
||||||
version_file = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'VERSION')
|
|
||||||
if os.path.exists(version_file):
|
|
||||||
with open(version_file, 'r') as f:
|
|
||||||
version = f.read().strip()
|
|
||||||
except Exception:
|
|
||||||
pass
|
|
||||||
|
|
||||||
# Import has_permission and is_feature_enabled from routes after routes are registered
|
# Import has_permission and is_feature_enabled from routes after routes are registered
|
||||||
from routes import has_permission, is_feature_enabled
|
from routes import has_permission, is_feature_enabled
|
||||||
|
|
||||||
return {
|
return {
|
||||||
'NAME': os.environ.get('NAME', 'JDB-NET'),
|
'NAME': os.environ.get('NAME', 'JDB-NET'),
|
||||||
'LOGO_PNG': os.environ.get('LOGO_PNG', 'https://assets.s3.jdbnet.co.uk/logo/128x128.png'),
|
'LOGO_PNG': os.environ.get('LOGO_PNG', 'https://assets.jdbnet.co.uk/logo/128x128.png'),
|
||||||
'VERSION': version,
|
'VERSION': version,
|
||||||
'has_permission': has_permission,
|
'has_permission': has_permission,
|
||||||
'is_feature_enabled': is_feature_enabled
|
'is_feature_enabled': is_feature_enabled
|
||||||
|
|||||||
@@ -1,64 +0,0 @@
|
|||||||
apiVersion: apps/v1
|
|
||||||
kind: Deployment
|
|
||||||
metadata:
|
|
||||||
name: ipam
|
|
||||||
namespace: ipam
|
|
||||||
spec:
|
|
||||||
replicas: 1
|
|
||||||
selector:
|
|
||||||
matchLabels:
|
|
||||||
app: ipam
|
|
||||||
template:
|
|
||||||
metadata:
|
|
||||||
labels:
|
|
||||||
app: ipam
|
|
||||||
spec:
|
|
||||||
containers:
|
|
||||||
- name: ipam
|
|
||||||
image: ghcr.io/jdb-net/ipam:staging
|
|
||||||
imagePullPolicy: Always
|
|
||||||
ports:
|
|
||||||
- containerPort: 5000
|
|
||||||
name: "ipam"
|
|
||||||
env:
|
|
||||||
- name: SECRET_KEY
|
|
||||||
value: "41TbN7v5peFLZPrdwSCc64J3mjmiUk5fkVWsmb2m"
|
|
||||||
- name: MYSQL_HOST
|
|
||||||
value: "10.10.25.4"
|
|
||||||
- name: MYSQL_USER
|
|
||||||
value: "ipam"
|
|
||||||
- name: MYSQL_PASSWORD
|
|
||||||
value: "WXPmo05sGCfjGe"
|
|
||||||
- name: MYSQL_DATABASE
|
|
||||||
value: "ipam"
|
|
||||||
---
|
|
||||||
apiVersion: v1
|
|
||||||
kind: Service
|
|
||||||
metadata:
|
|
||||||
name: ipam-ingress-service
|
|
||||||
namespace: ipam
|
|
||||||
spec:
|
|
||||||
selector:
|
|
||||||
app: ipam
|
|
||||||
ports:
|
|
||||||
- protocol: TCP
|
|
||||||
port: 80
|
|
||||||
targetPort: 5000
|
|
||||||
---
|
|
||||||
apiVersion: networking.k8s.io/v1
|
|
||||||
kind: Ingress
|
|
||||||
metadata:
|
|
||||||
name: ipam-ingress
|
|
||||||
namespace: ipam
|
|
||||||
spec:
|
|
||||||
rules:
|
|
||||||
- host: ipam.jdb143.uk
|
|
||||||
http:
|
|
||||||
paths:
|
|
||||||
- pathType: Prefix
|
|
||||||
path: "/"
|
|
||||||
backend:
|
|
||||||
service:
|
|
||||||
name: ipam-ingress-service
|
|
||||||
port:
|
|
||||||
number: 80
|
|
||||||
@@ -2605,7 +2605,7 @@ def register_routes(app, limiter=None):
|
|||||||
@app.route('/check_update')
|
@app.route('/check_update')
|
||||||
@login_required
|
@login_required
|
||||||
def check_update():
|
def check_update():
|
||||||
"""Check for available updates from GitHub (cached for 3 hours)"""
|
"""Check for available updates from Gitea (cached for 3 hours)"""
|
||||||
cache_key = 'check_update'
|
cache_key = 'check_update'
|
||||||
|
|
||||||
# Check cache first
|
# Check cache first
|
||||||
@@ -2614,15 +2614,11 @@ def register_routes(app, limiter=None):
|
|||||||
return jsonify(cached_result)
|
return jsonify(cached_result)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# Get current version
|
# Get current version from environment
|
||||||
version_file = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'VERSION')
|
current_version = os.environ.get('VERSION', 'unknown').lstrip('v')
|
||||||
current_version = 'unknown'
|
|
||||||
if os.path.exists(version_file):
|
|
||||||
with open(version_file, 'r') as f:
|
|
||||||
current_version = f.read().strip()
|
|
||||||
|
|
||||||
# Fetch latest release from GitHub
|
# Fetch latest release from Gitea
|
||||||
response = requests.get('https://api.github.com/repos/JDB-NET/ipam/releases/latest', timeout=5)
|
response = requests.get('https://git.jdbnet.co.uk/api/v1/repos/jamie/ipam/releases/latest', timeout=5)
|
||||||
if response.status_code != 200:
|
if response.status_code != 200:
|
||||||
return jsonify({'error': 'Failed to fetch release information'}), 500
|
return jsonify({'error': 'Failed to fetch release information'}), 500
|
||||||
|
|
||||||
|
|||||||
+88
-4
@@ -1,12 +1,96 @@
|
|||||||
document.addEventListener('DOMContentLoaded', function() {
|
document.addEventListener('DOMContentLoaded', function() {
|
||||||
const navToggle = document.getElementById('nav-toggle');
|
const navToggle = document.getElementById('nav-toggle');
|
||||||
const mobileNav = document.getElementById('mobile-nav');
|
const mobileNav = document.getElementById('mobile-nav');
|
||||||
navToggle.addEventListener('click', function() {
|
const searchModal = document.getElementById('search-modal');
|
||||||
mobileNav.classList.toggle('hidden');
|
const searchModalOpen = document.getElementById('search-modal-open');
|
||||||
});
|
const searchModalOpenMobile = document.getElementById('search-modal-open-mobile');
|
||||||
|
const searchModalClose = document.getElementById('search-modal-close');
|
||||||
|
const searchModalBackdrop = document.getElementById('search-modal-backdrop');
|
||||||
|
const searchModalInput = document.getElementById('search-modal-input');
|
||||||
|
|
||||||
|
if (navToggle && mobileNav) {
|
||||||
|
navToggle.addEventListener('click', function() {
|
||||||
|
mobileNav.classList.toggle('hidden');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function openSearchModal() {
|
||||||
|
if (!searchModal) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
searchModal.classList.remove('hidden');
|
||||||
|
searchModal.classList.add('flex');
|
||||||
|
document.body.classList.add('overflow-hidden');
|
||||||
|
|
||||||
|
if (mobileNav) {
|
||||||
|
mobileNav.classList.add('hidden');
|
||||||
|
}
|
||||||
|
|
||||||
|
setTimeout(function() {
|
||||||
|
if (searchModalInput) {
|
||||||
|
searchModalInput.focus();
|
||||||
|
searchModalInput.select();
|
||||||
|
}
|
||||||
|
}, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
function closeSearchModal() {
|
||||||
|
if (!searchModal) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
searchModal.classList.add('hidden');
|
||||||
|
searchModal.classList.remove('flex');
|
||||||
|
document.body.classList.remove('overflow-hidden');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (searchModalOpen) {
|
||||||
|
searchModalOpen.addEventListener('click', openSearchModal);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (searchModalOpenMobile) {
|
||||||
|
searchModalOpenMobile.addEventListener('click', openSearchModal);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (searchModalClose) {
|
||||||
|
searchModalClose.addEventListener('click', closeSearchModal);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (searchModalBackdrop) {
|
||||||
|
searchModalBackdrop.addEventListener('click', closeSearchModal);
|
||||||
|
}
|
||||||
|
|
||||||
document.addEventListener('click', function(e) {
|
document.addEventListener('click', function(e) {
|
||||||
if (!mobileNav.contains(e.target) && !navToggle.contains(e.target)) {
|
if (mobileNav && navToggle && !mobileNav.contains(e.target) && !navToggle.contains(e.target)) {
|
||||||
mobileNav.classList.add('hidden');
|
mobileNav.classList.add('hidden');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
document.addEventListener('keydown', function(e) {
|
||||||
|
const target = e.target;
|
||||||
|
const isEditableTarget = target && (
|
||||||
|
target.tagName === 'INPUT' ||
|
||||||
|
target.tagName === 'TEXTAREA' ||
|
||||||
|
target.tagName === 'SELECT' ||
|
||||||
|
target.isContentEditable
|
||||||
|
);
|
||||||
|
|
||||||
|
if (
|
||||||
|
e.key === '/' &&
|
||||||
|
!e.ctrlKey &&
|
||||||
|
!e.metaKey &&
|
||||||
|
!e.altKey &&
|
||||||
|
searchModal &&
|
||||||
|
!isEditableTarget
|
||||||
|
) {
|
||||||
|
e.preventDefault();
|
||||||
|
openSearchModal();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (e.key === 'Escape') {
|
||||||
|
closeSearchModal();
|
||||||
|
}
|
||||||
|
});
|
||||||
});
|
});
|
||||||
Vendored
+1
-1
@@ -1 +1 @@
|
|||||||
document.addEventListener("DOMContentLoaded",function(){let t=document.getElementById("nav-toggle"),e=document.getElementById("mobile-nav");t.addEventListener("click",function(){e.classList.toggle("hidden")}),document.addEventListener("click",function(n){e.contains(n.target)||t.contains(n.target)||e.classList.add("hidden")})});
|
document.addEventListener("DOMContentLoaded",function(){let e=document.getElementById("nav-toggle"),t=document.getElementById("mobile-nav"),o=document.getElementById("search-modal"),n=document.getElementById("search-modal-open"),d=document.getElementById("search-modal-open-mobile"),c=document.getElementById("search-modal-close"),l=document.getElementById("search-modal-backdrop"),a=document.getElementById("search-modal-input");function s(){o&&(o.classList.remove("hidden"),o.classList.add("flex"),document.body.classList.add("overflow-hidden"),t&&t.classList.add("hidden"),setTimeout(function(){a&&(a.focus(),a.select())},0))}function i(){o&&(o.classList.add("hidden"),o.classList.remove("flex"),document.body.classList.remove("overflow-hidden"))}e&&t&&e.addEventListener("click",function(){t.classList.toggle("hidden")}),n&&n.addEventListener("click",s),d&&d.addEventListener("click",s),c&&c.addEventListener("click",i),l&&l.addEventListener("click",i),document.addEventListener("click",function(o){t&&e&&!t.contains(o.target)&&!e.contains(o.target)&&t.classList.add("hidden")}),document.addEventListener("keydown",function(e){let t=e.target,n=t&&("INPUT"===t.tagName||"TEXTAREA"===t.tagName||"SELECT"===t.tagName||t.isContentEditable);if("/"===e.key&&!e.ctrlKey&&!e.metaKey&&!e.altKey&&o&&!n)return e.preventDefault(),void s();"Escape"===e.key&&i()})});
|
||||||
@@ -16,12 +16,12 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||||||
const compareLink = document.getElementById('toast-compare-link');
|
const compareLink = document.getElementById('toast-compare-link');
|
||||||
const closeBtn = document.getElementById('toast-close');
|
const closeBtn = document.getElementById('toast-close');
|
||||||
|
|
||||||
// Set versions
|
// Set versions (don't add 'v' prefix for dev versions)
|
||||||
currentVersionEl.textContent = 'v' + data.current_version;
|
currentVersionEl.textContent = (data.current_version === 'dev' ? '' : 'v') + data.current_version;
|
||||||
latestVersionEl.textContent = 'v' + data.latest_version;
|
latestVersionEl.textContent = (data.latest_version === 'dev' ? '' : 'v') + data.latest_version;
|
||||||
|
|
||||||
// Set compare link (current version to latest version)
|
// Set compare link (current version to latest version)
|
||||||
compareLink.href = `https://github.com/JDB-NET/ipam/compare/v${data.current_version}...v${data.latest_version}`;
|
compareLink.href = `https://git.jdbnet.co.uk/jamie/ipam/compare/v${data.current_version}...v${data.latest_version}`;
|
||||||
|
|
||||||
// Show toast
|
// Show toast
|
||||||
toast.classList.remove('hidden');
|
toast.classList.remove('hidden');
|
||||||
|
|||||||
Vendored
+1
-1
@@ -1 +1 @@
|
|||||||
document.addEventListener("DOMContentLoaded",function(){let t=sessionStorage.getItem("update-toast-dismissed");!t&&fetch("/check_update").then(t=>t.json()).then(t=>{if(t.update_available){let e=document.getElementById("update-toast"),n=document.getElementById("toast-current-version"),s=document.getElementById("toast-latest-version"),a=document.getElementById("toast-compare-link"),o=document.getElementById("toast-close");n.textContent="v"+t.current_version,s.textContent="v"+t.latest_version,a.href=`https://github.com/JDB-NET/ipam/compare/v${t.current_version}...v${t.latest_version}`,e.classList.remove("hidden"),o.addEventListener("click",function(){e.classList.add("hidden"),sessionStorage.setItem("update-toast-dismissed","true")})}}).catch(t=>{console.error("Error checking for updates:",t)})});
|
document.addEventListener("DOMContentLoaded",function(){let e=sessionStorage.getItem("update-toast-dismissed");!e&&fetch("/check_update").then(e=>e.json()).then(e=>{if(e.update_available){let t=document.getElementById("update-toast"),n=document.getElementById("toast-current-version"),s=document.getElementById("toast-latest-version"),a=document.getElementById("toast-compare-link"),d=document.getElementById("toast-close");n.textContent=("dev"===e.current_version?"":"v")+e.current_version,s.textContent=("dev"===e.latest_version?"":"v")+e.latest_version,a.href=`https://git.jdbnet.co.uk/jamie/ipam/compare/v${e.current_version}...v${e.latest_version}`,t.classList.remove("hidden"),d.addEventListener("click",function(){t.classList.add("hidden"),sessionStorage.setItem("update-toast-dismissed","true")})}}).catch(e=>{console.error("Error checking for updates:",e)})});
|
||||||
+44
-26
@@ -4,19 +4,15 @@
|
|||||||
<img src="{{ LOGO_PNG }}" alt="Logo" class="h-8 rounded">
|
<img src="{{ LOGO_PNG }}" alt="Logo" class="h-8 rounded">
|
||||||
<span class="text-2xl font-bold text-white whitespace-nowrap">{{ NAME }} IPAM</span>
|
<span class="text-2xl font-bold text-white whitespace-nowrap">{{ NAME }} IPAM</span>
|
||||||
</a>
|
</a>
|
||||||
<a href="https://github.com/JDB-NET/ipam/releases" target="_blank" rel="noopener noreferrer" class="text-sm font-normal text-gray-300 hover:text-gray-100 -ml-1 mt-3">v{{ VERSION }}</a>
|
<a href="https://git.jdbnet.co.uk/jamie/ipam/releases" target="_blank" rel="noopener noreferrer" class="text-sm font-normal text-gray-300 hover:text-gray-100 -ml-1 mt-3">{{ VERSION }}</a>
|
||||||
</div>
|
|
||||||
<div class="hidden lg:flex items-center justify-center absolute left-1/2" style="transform: translateX(calc(-50% + 1.5rem));">
|
|
||||||
<form action="/search" method="GET" class="flex items-center space-x-2">
|
|
||||||
<input type="text" name="q" id="search-input" placeholder="Search..."
|
|
||||||
class="bg-zinc-700 text-white placeholder-gray-400 px-4 py-2 rounded-md text-base focus:outline-none focus:ring-2 focus:ring-gray-500 w-100"
|
|
||||||
value="{{ request.args.get('q', '') }}">
|
|
||||||
<button type="submit" class="text-gray-200 hover:text-gray-400 hover:cursor-pointer flex-shrink-0">
|
|
||||||
<i class="fas fa-search"></i>
|
|
||||||
</button>
|
|
||||||
</form>
|
|
||||||
</div>
|
</div>
|
||||||
<nav class="hidden lg:flex items-center space-x-6 flex-shrink-0" id="main-nav">
|
<nav class="hidden lg:flex items-center space-x-6 flex-shrink-0" id="main-nav">
|
||||||
|
{% if current_user_name %}
|
||||||
|
<button type="button" class="text-gray-200 hover:text-gray-400 font-medium flex items-center gap-2 hover:cursor-pointer" id="search-modal-open" aria-label="Open search modal">
|
||||||
|
<i class="fas fa-search"></i>
|
||||||
|
<span>Search</span>
|
||||||
|
</button>
|
||||||
|
{% endif %}
|
||||||
{% if has_permission('view_index') %}
|
{% if has_permission('view_index') %}
|
||||||
<a href="/" class="text-gray-200 hover:text-gray-400 font-medium flex items-center gap-2">
|
<a href="/" class="text-gray-200 hover:text-gray-400 font-medium flex items-center gap-2">
|
||||||
<i class="fas fa-home"></i>
|
<i class="fas fa-home"></i>
|
||||||
@@ -58,22 +54,19 @@
|
|||||||
</a>
|
</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</nav>
|
</nav>
|
||||||
<button class="lg:hidden flex items-center text-gray-200 hover:cursor-pointer focus:outline-none flex-shrink-0" id="nav-toggle" aria-label="Open navigation menu">
|
<div class="lg:hidden flex items-center gap-3 flex-shrink-0">
|
||||||
<svg class="w-8 h-8" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
{% if current_user_name %}
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 12h16M4 18h16"></path>
|
<button class="flex items-center text-gray-200 hover:cursor-pointer focus:outline-none" id="search-modal-open-mobile" aria-label="Open search modal">
|
||||||
</svg>
|
<i class="fas fa-search text-xl"></i>
|
||||||
</button>
|
</button>
|
||||||
|
{% endif %}
|
||||||
|
<button class="flex items-center text-gray-200 hover:cursor-pointer focus:outline-none" id="nav-toggle" aria-label="Open navigation menu">
|
||||||
|
<svg class="w-8 h-8" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 12h16M4 18h16"></path>
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
<div class="lg:hidden fixed top-13 left-0 right-0 bg-zinc-800 shadow-lg z-50 w-full hidden flex-col py-2" id="mobile-nav">
|
<div class="lg:hidden fixed top-13 left-0 right-0 bg-zinc-800 shadow-lg z-50 w-full hidden flex-col py-2" id="mobile-nav">
|
||||||
<form action="/search" method="GET" class="px-4 py-2 border-b border-zinc-700">
|
|
||||||
<div class="flex items-center space-x-2">
|
|
||||||
<input type="text" name="q" placeholder="Search..."
|
|
||||||
class="bg-zinc-700 text-white placeholder-gray-400 px-3 py-2.5 rounded text-base focus:outline-none focus:ring-2 focus:ring-gray-500 flex-1"
|
|
||||||
value="{{ request.args.get('q', '') }}">
|
|
||||||
<button type="submit" class="text-gray-200 hover:text-gray-400 hover:cursor-pointer flex-shrink-0">
|
|
||||||
<i class="fas fa-search"></i>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
{% if has_permission('view_index') %}
|
{% if has_permission('view_index') %}
|
||||||
<a href="/" class="block px-6 py-2 text-gray-200 hover:text-gray-400 font-medium flex items-center gap-2">
|
<a href="/" class="block px-6 py-2 text-gray-200 hover:text-gray-400 font-medium flex items-center gap-2">
|
||||||
<i class="fas fa-home"></i>
|
<i class="fas fa-home"></i>
|
||||||
@@ -115,6 +108,31 @@
|
|||||||
</a>
|
</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{% if current_user_name %}
|
||||||
|
<div id="search-modal" class="hidden fixed inset-0 z-50 items-center justify-center p-4">
|
||||||
|
<div id="search-modal-backdrop" class="absolute inset-0 bg-black/60"></div>
|
||||||
|
<div class="relative w-full max-w-2xl rounded-lg bg-zinc-800 border border-zinc-700 shadow-xl">
|
||||||
|
<div class="flex items-center justify-between px-4 py-3 border-b border-zinc-700">
|
||||||
|
<h3 class="text-white font-semibold text-lg">Search</h3>
|
||||||
|
<button type="button" id="search-modal-close" class="text-gray-300 hover:text-white hover:cursor-pointer" aria-label="Close search modal">
|
||||||
|
<i class="fas fa-times"></i>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<form action="/search" method="GET" class="p-4">
|
||||||
|
<div class="flex items-center gap-2">
|
||||||
|
<input type="text" name="q" id="search-modal-input" placeholder="Search..."
|
||||||
|
class="bg-zinc-700 text-white placeholder-gray-400 px-4 py-2 rounded-md text-base focus:outline-none focus:ring-2 focus:ring-gray-500 w-full"
|
||||||
|
value="{{ request.args.get('q', '') }}">
|
||||||
|
<button type="submit" class="text-gray-200 hover:text-gray-400 hover:cursor-pointer flex-shrink-0" aria-label="Submit search">
|
||||||
|
<i class="fas fa-search"></i>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
<script src="/static/js/header.min.js"></script>
|
<script src="/static/js/header.min.js"></script>
|
||||||
|
|
||||||
<!-- Update Available Toast -->
|
<!-- Update Available Toast -->
|
||||||
|
|||||||
Reference in New Issue
Block a user