Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
61 commits
Select commit Hold shift + click to select a range
fb4b542
chore: configure base env to run it on NIXOS
juancolchete Jan 13, 2026
b7c5018
feat: add GetURLParameter
juancolchete Jan 14, 2026
20b252b
feat: mount issue get data request
juancolchete Jan 14, 2026
8ca521f
feat: add gitea route
juancolchete Jan 14, 2026
7e37fbf
feat: add api route to fetch from gitea
juancolchete Jan 14, 2026
5bc9ee9
feat: return base structure
juancolchete Jan 14, 2026
0eb82b4
feat: process issue data to get community name
juancolchete Jan 14, 2026
19fd07f
feat: add icon
juancolchete Jan 14, 2026
94b03c0
feat: add lightning tips
juancolchete Jan 14, 2026
9010a84
feat: add contact
juancolchete Jan 14, 2026
99c174a
feat: add description
juancolchete Jan 14, 2026
6c8964e
feat: prefil name on tags form
juancolchete Jan 14, 2026
d850019
feat: add suggested logo
juancolchete Jan 14, 2026
468a7ae
feat: prefill lightning tips on tags form
juancolchete Jan 14, 2026
c99e13d
fix: cannot set value of undefined
juancolchete Jan 15, 2026
91fb972
feat: add discord contact
juancolchete Jan 15, 2026
2f4f80d
feat: add email contact
juancolchete Jan 15, 2026
4927234
feat: add facebook contact
juancolchete Jan 15, 2026
27f8413
feat: add geyser contact
juancolchete Jan 15, 2026
bb8fb77
feat: add github contact
juancolchete Jan 15, 2026
1d16fc5
feat: add instagram contact
juancolchete Jan 15, 2026
53c797d
feat: add linkedin contact
juancolchete Jan 15, 2026
7755562
feat: add matrix contact
juancolchete Jan 15, 2026
6246f18
feat: add meetup contact
juancolchete Jan 15, 2026
9e17df5
feat: add nostr contact
juancolchete Jan 15, 2026
be28ab3
feat: add phone contact
juancolchete Jan 15, 2026
3df5a27
feat: add rss contact
juancolchete Jan 15, 2026
6a5d071
feat: add signal contact
juancolchete Jan 15, 2026
631d8f4
feat: add telegram contact
juancolchete Jan 15, 2026
8390724
feat: add twitter contact
juancolchete Jan 15, 2026
7b099d8
feat: add website contact
juancolchete Jan 15, 2026
03de405
feat: add whatsapp contact
juancolchete Jan 15, 2026
065196f
feat: add youtube contact
juancolchete Jan 15, 2026
354672d
feat: add description to tags form
juancolchete Jan 15, 2026
b74f979
feat: validate lightning tips length
juancolchete Jan 15, 2026
14db297
feat: validate description length
juancolchete Jan 15, 2026
6aa661b
feat: add url alias
juancolchete Jan 15, 2026
636f6b7
refactor: prefill issue data logic
juancolchete Jan 15, 2026
6231861
refactor: gitea get-issue API error handle
juancolchete Jan 29, 2026
f034e62
fix: 2. Frontend: Inconsistent Session Handling
juancolchete Jan 30, 2026
1f083bf
fix: 3. XSS Vulnerability: Unescaped HTML Injection
juancolchete Jan 30, 2026
1e13cce
fix: 4. Fragile String Parsing
juancolchete Jan 30, 2026
4cb4431
fix: 6. URL Parameter Not Decoded
juancolchete Jan 30, 2026
b16e7e8
refactor: remove comment
juancolchete Jan 30, 2026
7e1e9d0
fix: 7. Weak Contact Field Detection
juancolchete Jan 30, 2026
16c548f
fix: 8. Inefficient String Replacement
juancolchete Jan 30, 2026
f14f16d
fix: 9. Magic Numbers Without Explanation
juancolchete Jan 30, 2026
4a57514
fix: Enhance Existing Server-Side Validation
juancolchete Jan 30, 2026
3c6049f
fix: not getting community name
juancolchete Jan 30, 2026
e5b7f5f
fix: fill fields id passing issue execution order
juancolchete Jan 30, 2026
7991f80
refactor: 11. Inconsistent Naming Conventions
juancolchete Feb 1, 2026
9c296f0
refactor: 12. Missing Documentation
juancolchete Feb 1, 2026
aeef449
fix: remove arbitrary innerhtml
juancolchete Mar 11, 2026
bbeb608
fix: Fragile string parsing — The indexOf() chain parsing approach fa…
juancolchete Mar 11, 2026
6f47c42
fix: inner html xss injection on createTagRow
juancolchete Mar 11, 2026
43fcc34
Merge branch 'main' into main
juancolchete Apr 5, 2026
6dc9302
chore: ignore venv
juancolchete Apr 5, 2026
12a79ef
chore: apply cosmetic AI changes
juancolchete Apr 5, 2026
5dc3d10
feat: apply copilot sugestions
juancolchete Apr 8, 2026
bf87e30
chore: simplify requirements
juancolchete Apr 9, 2026
538cf7f
fix: Gitea issue fetching to use base URL config based on rabbit sugg…
juancolchete Apr 9, 2026
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
1 change: 1 addition & 0 deletions .env.sample
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
GITEA_BASE_URL=https://gitea.btcmap.org
3 changes: 3 additions & 0 deletions .envrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
use flake

printf 'GITEA_BASE_URL=%s\n' "${GITEA_BASE_URL-}" > .direnv/.env
4 changes: 2 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
__pycache__/
*.py[cod]
*$py.class
venv

# uv virtual environment
.venv/
Expand Down Expand Up @@ -38,6 +39,5 @@ replit.nix
# Others
*.log
*.sqlite
opencode.json
.direnv
.env
users.json
89 changes: 59 additions & 30 deletions app.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
app.config['SESSION_COOKIE_SECURE'] = True
app.config['SESSION_COOKIE_HTTPONLY'] = True
app.config['SESSION_COOKIE_SAMESITE'] = 'Lax'
app.config['GITEA_BASE_URL'] = os.environ.get('GITEA_BASE_URL', 'https://gitea.btcmap.org')

# Configure server-side sessions (filesystem backend)
app.config['SESSION_TYPE'] = 'filesystem'
Expand Down Expand Up @@ -71,6 +72,9 @@ def inject_current_user():
},
'url_alias': {
'required': True,
'min_length': 2,
'max_length': 100,
'pattern': r'^[a-z0-9\-]+$',
'type': 'text'
},
'continent': {
Expand Down Expand Up @@ -104,7 +108,8 @@ def inject_current_user():
},
'contact:twitter': {
'required': False,
'type': 'url'
'type': 'url',
'pattern': r'(?:x\.com|twitter\.com)\/[\w]+'
},
'contact:website': {
'required': False,
Expand All @@ -116,63 +121,77 @@ def inject_current_user():
},
'contact:telegram': {
'required': False,
'type': 'url'
'type': 'url',
'pattern': r't\.me\/[\w]+'
},
'contact:signal': {
'required': False,
'type': 'url'
'type': 'url',
'pattern': r'signal\.group\/'
},
'contact:whatsapp': {
'required': False,
'type': 'url'
'type': 'url',
'pattern': r'chat\.whatsapp\.com\/'
},
'contact:nostr': {
'required': False,
'type': 'text'
'type': 'text',
'pattern': r'^npub1[a-z0-9]{58}$'
},
'contact:meetup': {
'required': False,
'type': 'url'
'type': 'url',
'pattern': r'meetup\.com\/'
},
'contact:discord': {
'required': False,
'type': 'url'
'type': 'url',
'pattern': r'discord\.gg|discord\.com'
},
'contact:instagram': {
'required': False,
'type': 'url'
'type': 'url',
'pattern': r'instagram\.com\/[\w._]+'
},
'contact:youtube': {
'required': False,
'type': 'url'
'type': 'url',
'pattern': r'(?:youtu\.be|youtube\.com)\/'
},
'contact:facebook': {
'required': False,
'type': 'url'
'type': 'url',
'pattern': r'facebook\.com\/[\w.]+'
},
'contact:linkedin': {
'required': False,
'type': 'url'
'type': 'url',
'pattern': r'linkedin\.com\/'
},
'contact:rss': {
'required': False,
'type': 'url'
'type': 'url',
'pattern': r'\/(?:feed|rss)'
},
'contact:phone': {
'required': False,
'type': 'tel'
},
'contact:github': {
'required': False,
'type': 'url'
'type': 'url',
'pattern': r'github\.com\/[\w-]+'
},
'contact:matrix': {
'required': False,
'type': 'url'
'type': 'text',
'pattern': r'@[\w._-]+:[\w.-]+'
},
'contact:geyser': {
'required': False,
'type': 'url'
'type': 'url',
'pattern': r'geyser\.fund\/'
},
'contact:eventbrite': {
'required': False,
Expand All @@ -192,11 +211,15 @@ def inject_current_user():
},
'tips:lightning_address': {
'required': False,
'type': 'text'
'type': 'text',
'min_length': 10,
'pattern': r'^[\w\.\-]+@[\w\.\-]+\.[a-zA-Z]{2,}$'
},
'description': {
'required': False,
'type': 'text'
'type': 'text',
'min_length': 10,
'max_length': 500
}
}
}
Expand Down Expand Up @@ -1154,19 +1177,25 @@ def lint_community_orgs():
@app.route('/api/gitea/get-issue/<int:issue_id>')
@login_required
def get_issue_data(issue_id):
try:
req_data = requests.get(
f"https://gitea.btcmap.org/api/v1/repos/teambtcmap/btcmap-data/issues/{issue_id}",
timeout=15,
)
req_data.raise_for_status()
return jsonify({'data': req_data.json()})
except requests.exceptions.Timeout:
return jsonify({'error': 'Request to Gitea timed out'}), 408
except requests.exceptions.RequestException as exc:
app.logger.error(f"Error fetching Gitea issue {issue_id}: {exc}")
return jsonify({'error': 'Failed to fetch issue data'}), 502

try:
base_url = app.config['GITEA_BASE_URL'].rstrip('/')
req_data = requests.get(
f"{base_url}/api/v1/repos/teambtcmap/btcmap-data/issues/{issue_id}",
timeout=15,
)
req_data.raise_for_status()
return jsonify({'data': req_data.json()})
except requests.exceptions.HTTPError as exc:
status_code = exc.response.status_code if exc.response is not None else 502
app.logger.error(f"Error fetching Gitea issue {issue_id}: {exc}")
if status_code == 404:
return jsonify({'error': 'Issue not found'}), 404
return jsonify({'error': 'Failed to fetch issue data'}), 502
except requests.exceptions.Timeout:
return jsonify({'error': 'Request to Gitea timed out'}), 408
except requests.exceptions.RequestException as exc:
app.logger.error(f"Error fetching Gitea issue {issue_id}: {exc}")
return jsonify({'error': 'Failed to fetch issue data'}), 502
def get_area(area_id):
result = rpc_call('get_area', {'id': area_id})
if 'error' not in result:
Expand Down
27 changes: 27 additions & 0 deletions flake.lock

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

28 changes: 28 additions & 0 deletions flake.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
{
description = "BTCMAP Admin page";

inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-25.05";
};

outputs = { self, nixpkgs }:
let
system = "x86_64-linux";
pkgs = import nixpkgs { inherit system; };
in {
devShells.${system}.default = pkgs.mkShell {
name = "btcmap-admin";

buildInputs = with pkgs; [
python311
python311Packages.virtualenv
geos
];
LD_LIBRARY_PATH = pkgs.lib.makeLibraryPath [
pkgs.stdenv.cc.cc.lib
pkgs.zlib
];
};
};
}

9 changes: 9 additions & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
flask
flask_session
Flask-Login
requests
geojson_rewind
shapely
pyproj
nostr-sdk
cryptography
19 changes: 19 additions & 0 deletions static/js/utils.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
/**
* Passing the name of URL parameter we get its value
* @param {string} sParam - The search string also known as URL parameter
* @returns {string} - The URL parameter content
* @throws {}
*/
function getURLParameter(sParam) {
const params = new URLSearchParams(window.location.search)
return params.get(sParam)
}
/**
* Passing a text without formatation, this text is normalized to get its main data
* @param {string} text - Unformated text
* @returns {string} - Formatted text
* @throws {}
*/
function cleanIssueText(text) {
return text?.replace(/\n/g, ' ').trim() || ''
}
Loading