Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Contribute to GitLab
Sign in
Toggle navigation
A
aisbf
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
nexlab
aisbf
Commits
f65694ef
Commit
f65694ef
authored
Apr 22, 2026
by
Stefy Lanza (nextime / spora )
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
00.99.48
parent
13b80367
Changes
9
Hide whitespace changes
Inline
Side-by-side
Showing
9 changed files
with
77 additions
and
34 deletions
+77
-34
CHANGELOG.md
CHANGELOG.md
+2
-2
PYPI.md
PYPI.md
+3
-3
__init__.py
aisbf/__init__.py
+1
-1
database.py
aisbf/database.py
+12
-2
paypal_handler.py
aisbf/payments/fiat/paypal_handler.py
+29
-15
main.py
main.py
+6
-2
pyproject.toml
pyproject.toml
+1
-1
setup.py
setup.py
+1
-1
wallet.html
templates/dashboard/wallet.html
+22
-7
No files found.
CHANGELOG.md
View file @
f65694ef
...
@@ -7,7 +7,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
...
@@ -7,7 +7,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased]
## [Unreleased]
## [0.99.4
7
] - 2026-04-21
## [0.99.4
8
] - 2026-04-21
### Added
### Added
-
**Unified Wallet System**
-
**Unified Wallet System**
...
@@ -29,7 +29,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
...
@@ -29,7 +29,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
-
Clarified token access patterns (global vs user tokens)
-
Clarified token access patterns (global vs user tokens)
### Changed
### Changed
-
Updated all version numbers to 0.99.4
7
-
Updated all version numbers to 0.99.4
8
-
Added wallet package to pyproject.toml
-
Added wallet package to pyproject.toml
## [0.99.26] - 2026-04-16
## [0.99.26] - 2026-04-16
...
...
PYPI.md
View file @
f65694ef
...
@@ -41,8 +41,8 @@ python -m build
...
@@ -41,8 +41,8 @@ python -m build
```
```
This creates:
This creates:
- `dist/aisbf-0.99.4
7
.tar.gz` - Source distribution
- `dist/aisbf-0.99.4
8
.tar.gz` - Source distribution
- `dist/aisbf-0.99.4
7
-py3-none-any.whl` - Wheel distribution
- `dist/aisbf-0.99.4
8
-py3-none-any.whl` - Wheel distribution
## Testing the Package
## Testing the Package
...
@@ -50,7 +50,7 @@ This creates:
...
@@ -50,7 +50,7 @@ This creates:
```
bash
```
bash
# Install from the built wheel
# Install from the built wheel
pip install dist/aisbf-0.99.4
7
-py3-none-any.whl
pip install dist/aisbf-0.99.4
8
-py3-none-any.whl
# Test the installation
# Test the installation
aisbf status
aisbf status
...
...
aisbf/__init__.py
View file @
f65694ef
...
@@ -54,7 +54,7 @@ from .auth.qwen import QwenOAuth2
...
@@ -54,7 +54,7 @@ from .auth.qwen import QwenOAuth2
from
.handlers
import
RequestHandler
,
RotationHandler
,
AutoselectHandler
from
.handlers
import
RequestHandler
,
RotationHandler
,
AutoselectHandler
from
.utils
import
count_messages_tokens
,
split_messages_into_chunks
,
get_max_request_tokens_for_model
from
.utils
import
count_messages_tokens
,
split_messages_into_chunks
,
get_max_request_tokens_for_model
__version__
=
"0.99.4
7
"
__version__
=
"0.99.4
8
"
__all__
=
[
__all__
=
[
# Config
# Config
"config"
,
"config"
,
...
...
aisbf/database.py
View file @
f65694ef
...
@@ -4334,8 +4334,18 @@ def DatabaseManager__run_config_migrations(self, cursor, auto_increment, timesta
...
@@ -4334,8 +4334,18 @@ def DatabaseManager__run_config_migrations(self, cursor, auto_increment, timesta
logger
.
info
(
"✅ Migration: Populated username, is_active and role for existing users"
)
logger
.
info
(
"✅ Migration: Populated username, is_active and role for existing users"
)
else
:
else
:
# MySQL migrations would go here
# MySQL: check and add any missing columns
pass
for
col_name
,
col_def
,
_
in
required_columns
:
try
:
cursor
.
execute
(
"""
SELECT COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'users' AND COLUMN_NAME =
%
s
"""
,
(
col_name
,))
if
not
cursor
.
fetchone
():
cursor
.
execute
(
f
'ALTER TABLE users ADD COLUMN {col_name} {col_def}'
)
logger
.
info
(
f
"✅ Migration: Added {col_name} column to users"
)
except
Exception
as
col_e
:
logger
.
warning
(
f
"Migration check for users.{col_name}: {col_e}"
)
except
Exception
as
e
:
except
Exception
as
e
:
logger
.
warning
(
f
"Migration check for users table columns: {e}"
)
logger
.
warning
(
f
"Migration check for users table columns: {e}"
)
...
...
aisbf/payments/fiat/paypal_handler.py
View file @
f65694ef
...
@@ -355,22 +355,36 @@ class PayPalPaymentHandler:
...
@@ -355,22 +355,36 @@ class PayPalPaymentHandler:
return
{
'status'
:
'error'
,
'message'
:
str
(
e
)}
return
{
'status'
:
'error'
,
'message'
:
str
(
e
)}
async
def
_verify_webhook_signature
(
self
,
payload
:
dict
,
headers
:
dict
)
->
bool
:
async
def
_verify_webhook_signature
(
self
,
payload
:
dict
,
headers
:
dict
)
->
bool
:
"""
"""Verify PayPal webhook signature via PayPal's verify-webhook-signature API."""
Verify PayPal webhook signature
webhook_id
=
self
.
webhook_secret
# stored as 'webhook_secret' in admin settings
if
not
webhook_id
:
For production, implement proper signature verification:
logger
.
warning
(
"PayPal webhook_id not configured - skipping signature verification"
)
https://developer.paypal.com/api/rest/webhooks/rest/#verify-webhook-signature
"""
# For now, basic verification - in production, verify the signature properly
if
not
self
.
webhook_secret
:
logger
.
warning
(
"PayPal webhook_secret not configured - skipping signature verification"
)
return
True
return
True
# TODO: Implement proper webhook signature verification
try
:
# This requires calling PayPal's verify-webhook-signature endpoint
access_token
=
await
self
.
get_access_token
()
# with the webhook_id, transmission_id, transmission_sig, etc.
body
=
{
"auth_algo"
:
headers
.
get
(
"paypal-auth-algo"
,
""
),
return
True
"cert_url"
:
headers
.
get
(
"paypal-cert-url"
,
""
),
"transmission_id"
:
headers
.
get
(
"paypal-transmission-id"
,
""
),
"transmission_sig"
:
headers
.
get
(
"paypal-transmission-sig"
,
""
),
"transmission_time"
:
headers
.
get
(
"paypal-transmission-time"
,
""
),
"webhook_id"
:
webhook_id
,
"webhook_event"
:
payload
,
}
response
=
await
self
.
http_client
.
post
(
f
"{self.base_url}/v1/notifications/verify-webhook-signature"
,
headers
=
{
"Authorization"
:
f
"Bearer {access_token}"
,
"Content-Type"
:
"application/json"
},
json
=
body
,
)
result
=
response
.
json
()
verified
=
result
.
get
(
"verification_status"
)
==
"SUCCESS"
if
not
verified
:
logger
.
warning
(
f
"PayPal webhook verification failed: {result}"
)
return
verified
except
Exception
as
e
:
logger
.
error
(
f
"PayPal webhook signature verification error: {e}"
)
return
False
async
def
_handle_order_completed
(
self
,
resource
:
dict
):
async
def
_handle_order_completed
(
self
,
resource
:
dict
):
"""Handle completed order (Vault v3)"""
"""Handle completed order (Vault v3)"""
...
...
main.py
View file @
f65694ef
...
@@ -1363,6 +1363,7 @@ async def startup_event():
...
@@ -1363,6 +1363,7 @@ async def startup_event():
payment_service
=
PaymentService
(
db_manager
,
payment_config
)
payment_service
=
PaymentService
(
db_manager
,
payment_config
)
await
payment_service
.
initialize
()
await
payment_service
.
initialize
()
app
.
state
.
payment_service
=
payment_service
logger
.
info
(
"Payment service started"
)
logger
.
info
(
"Payment service started"
)
except
Exception
as
e
:
except
Exception
as
e
:
...
@@ -3976,7 +3977,9 @@ async def dashboard_index(request: Request):
...
@@ -3976,7 +3977,9 @@ async def dashboard_index(request: Request):
if
request
.
session
.
get
(
'role'
)
==
'admin'
:
if
request
.
session
.
get
(
'role'
)
==
'admin'
:
# Admin dashboard
# Admin dashboard
return
templates
.
TemplateResponse
(
db
=
DatabaseRegistry
.
get_config_database
()
users_count
=
len
(
db
.
get_users
())
return
templates
.
TemplateResponse
(
request
=
request
,
request
=
request
,
name
=
"dashboard/index.html"
,
name
=
"dashboard/index.html"
,
context
=
{
context
=
{
...
@@ -3986,7 +3989,8 @@ async def dashboard_index(request: Request):
...
@@ -3986,7 +3989,8 @@ async def dashboard_index(request: Request):
"providers_count"
:
len
(
config
.
providers
)
if
config
else
0
,
"providers_count"
:
len
(
config
.
providers
)
if
config
else
0
,
"rotations_count"
:
len
(
config
.
rotations
)
if
config
else
0
,
"rotations_count"
:
len
(
config
.
rotations
)
if
config
else
0
,
"autoselect_count"
:
len
(
config
.
autoselect
)
if
config
else
0
,
"autoselect_count"
:
len
(
config
.
autoselect
)
if
config
else
0
,
"server_config"
:
server_config
or
{}
"server_config"
:
server_config
or
{},
"users_count"
:
users_count
,
}
}
)
)
else
:
else
:
...
...
pyproject.toml
View file @
f65694ef
...
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
...
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
[project]
[project]
name
=
"aisbf"
name
=
"aisbf"
version
=
"0.99.4
7
"
version
=
"0.99.4
8
"
description
=
"AISBF - AI Service Broker Framework || AI Should Be Free - A modular proxy server for managing multiple AI provider integrations"
description
=
"AISBF - AI Service Broker Framework || AI Should Be Free - A modular proxy server for managing multiple AI provider integrations"
readme
=
"README.md"
readme
=
"README.md"
license
=
"GPL-3.0-or-later"
license
=
"GPL-3.0-or-later"
...
...
setup.py
View file @
f65694ef
...
@@ -49,7 +49,7 @@ class InstallCommand(_install):
...
@@ -49,7 +49,7 @@ class InstallCommand(_install):
setup
(
setup
(
name
=
"aisbf"
,
name
=
"aisbf"
,
version
=
"0.99.4
7
"
,
version
=
"0.99.4
8
"
,
author
=
"AISBF Contributors"
,
author
=
"AISBF Contributors"
,
author_email
=
"stefy@nexlab.net"
,
author_email
=
"stefy@nexlab.net"
,
description
=
"AISBF - AI Service Broker Framework || AI Should Be Free - A modular proxy server for managing multiple AI provider integrations"
,
description
=
"AISBF - AI Service Broker Framework || AI Should Be Free - A modular proxy server for managing multiple AI provider integrations"
,
...
...
templates/dashboard/wallet.html
View file @
f65694ef
...
@@ -283,13 +283,27 @@ document.addEventListener('DOMContentLoaded', function () {
...
@@ -283,13 +283,27 @@ document.addEventListener('DOMContentLoaded', function () {
})
})
.
then
(
r
=>
r
.
json
())
.
then
(
r
=>
r
.
json
())
.
then
(
data
=>
{
.
then
(
data
=>
{
if
(
data
.
type
===
'stripe'
&&
data
.
checkout_url
)
{
this
.
disabled
=
false
;
window
.
location
.
href
=
data
.
checkout_url
;
this
.
innerHTML
=
orig
;
}
else
if
(
data
.
type
===
'paypal'
&&
data
.
approval_url
)
{
let
url
=
null
;
window
.
location
.
href
=
data
.
approval_url
;
if
(
data
.
type
===
'stripe'
&&
data
.
checkout_url
)
url
=
data
.
checkout_url
;
else
if
(
data
.
type
===
'paypal'
&&
data
.
approval_url
)
url
=
data
.
approval_url
;
if
(
url
)
{
const
popup
=
window
.
open
(
url
,
'payment_popup'
,
'width=600,height=700,scrollbars=yes,resizable=yes'
);
if
(
!
popup
)
{
// Blocked by browser — open in new tab
window
.
open
(
url
,
'_blank'
);
return
;
}
// Poll for popup close then reload wallet page
const
timer
=
setInterval
(()
=>
{
if
(
popup
.
closed
)
{
clearInterval
(
timer
);
window
.
location
.
reload
();
}
},
800
);
}
else
{
}
else
{
this
.
disabled
=
false
;
this
.
innerHTML
=
orig
;
showAlert
(
data
.
error
||
'Failed to initiate checkout.'
,
'Error'
,
'❌'
,
'danger'
);
showAlert
(
data
.
error
||
'Failed to initiate checkout.'
,
'Error'
,
'❌'
,
'danger'
);
}
}
})
})
...
@@ -435,7 +449,8 @@ function openCryptoModal(name) {
...
@@ -435,7 +449,8 @@ function openCryptoModal(name) {
document
.
getElementById
(
'cryptoModal'
).
classList
.
add
(
'active'
);
document
.
getElementById
(
'cryptoModal'
).
classList
.
add
(
'active'
);
// Fetch a per-user deposit address from the server
// Fetch a per-user deposit address from the server
const
amount
=
getAmount
()
||
15
;
const
customVal
=
parseFloat
(
document
.
getElementById
(
'custom-amount'
).
value
);
const
amount
=
customVal
||
selectedAmount
||
15
;
fetch
(
'/dashboard/wallet/topup'
,
{
fetch
(
'/dashboard/wallet/topup'
,
{
method
:
'POST'
,
method
:
'POST'
,
headers
:
{
'Content-Type'
:
'application/json'
},
headers
:
{
'Content-Type'
:
'application/json'
},
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment