Skip to content
This repository was archived by the owner on Mar 8, 2025. It is now read-only.

Commit

Permalink
Add ability to store media locally.
Browse files Browse the repository at this point in the history
  • Loading branch information
ibz authored Jan 17, 2024
1 parent b888d65 commit 9a78ad3
Show file tree
Hide file tree
Showing 9 changed files with 49 additions and 18 deletions.
1 change: 1 addition & 0 deletions .env.prod
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
ENV=prod
FLASK_APP=main
LOG_LEVEL=INFO
USE_S3=1
BIRDWATCHER_BASE_URL=http://birdwatcher:6000
LNDHUB_URL=https://ln.getalby.com
LOG_FILENAME=/state/pm.log
Expand Down
4 changes: 2 additions & 2 deletions api/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@

from extensions import db
import models as m
from main import app, get_birdwatcher, get_lndhub_client, get_s3, get_site_admin_config
from main import app, get_birdwatcher, get_lndhub_client, get_file_storage, get_site_admin_config
from main import get_token_from_request, get_user_from_token, user_required
from main import MempoolSpaceError
from nostr_utils import EventValidationError, validate_event
Expand Down Expand Up @@ -682,7 +682,7 @@ def post_media(key, cls, singular):

for f in request.files.values():
media = m.Media(item_id=entity.item_id, index=index)
if not media.store(get_s3(), f"{singular}_{entity.key}_media_{index}", f.filename, f.read()):
if not media.store(get_file_storage(), f"{singular}_{entity.key}_media_{index}", f.filename, f.read()):
return jsonify({'message': "Error saving picture!"}), 400
db.session.add(media)
index += 1
Expand Down
12 changes: 7 additions & 5 deletions api/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,11 +66,13 @@
MOCK_NOSTR = bool(int(os.environ.get("MOCK_NOSTR", 0)))

MOCK_S3 = bool(int(os.environ.get('MOCK_S3', 0)))
S3_SECRETS = "/secrets/s3.json"
S3_ENDPOINT_URL = "https://s3.us-west-004.backblazeb2.com"
S3_BUCKET = 'plebeian-market'
S3_FILENAME_PREFIX = os.environ.get('S3_FILENAME_PREFIX', "")
S3_URL_PREFIX = f"https://f004.backblazeb2.com/file/{S3_BUCKET}/"
USE_S3 = bool(int(os.environ.get('USE_S3', 0)))
if USE_S3:
S3_SECRETS = "/secrets/s3.json"
S3_ENDPOINT_URL = "https://s3.us-west-004.backblazeb2.com"
S3_BUCKET = 'plebeian-market'
S3_FILENAME_PREFIX = os.environ.get('S3_FILENAME_PREFIX', "")
S3_URL_PREFIX = f"https://f004.backblazeb2.com/file/{S3_BUCKET}/"

SATS_IN_BTC = 100000000

Expand Down
29 changes: 23 additions & 6 deletions api/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -794,7 +794,7 @@ def get_site_admin_config():
'lightning_address': "[email protected]",
}

class MockS3:
class MockFileStorage:
def get_url_prefix(self):
return app.config['API_BASE_URL_EXTERNAL'] + "/mock-s3-files/"

Expand All @@ -808,7 +808,7 @@ def upload(self, data, filename):
# basically store the content under /tmp to be used by the /mock-s3-files/ route later
f.write(data)

class S3:
class S3FileStorage:
def __init__(self, endpoint_url, key_id, application_key):
self.s3 = boto3.resource(service_name='s3', endpoint_url=endpoint_url, aws_access_key_id=key_id, aws_secret_access_key=application_key, config=Config(signature_version='s3v4'))

Expand All @@ -821,13 +821,30 @@ def get_filename_prefix(self):
def upload(self, data, filename):
self.s3.Bucket(app.config['S3_BUCKET']).upload_fileobj(io.BytesIO(data), self.get_filename_prefix() + filename)

def get_s3():
class LocalFileStorage:
def get_url_prefix(self):
return f"{app.config['WWW_BASE_URL']}/media/"

def get_filename_prefix(self):
return ""

def upload(self, data, filename):
filename_with_prefix = self.get_filename_prefix() + filename
app.logger.info(f"Uploading media: {filename_with_prefix}...")
if not os.path.exists("/state/media"):
os.makedirs("/state/media")
with open(f"/state/media/{filename_with_prefix}", "wb") as f:
f.write(data)

def get_file_storage():
if app.config['MOCK_S3']:
return MockS3()
else:
return MockFileStorage()
elif app.config['USE_S3']:
with open(app.config['S3_SECRETS']) as f:
s3_secrets = json.load(f)
return S3(app.config['S3_ENDPOINT_URL'], s3_secrets['KEY_ID'], s3_secrets['APPLICATION_KEY'])
return S3FileStorage(app.config['S3_ENDPOINT_URL'], s3_secrets['KEY_ID'], s3_secrets['APPLICATION_KEY'])
else:
return LocalFileStorage()

class Mail:
def send(self, to, subject, body, html):
Expand Down
4 changes: 2 additions & 2 deletions api/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -811,8 +811,8 @@ def to_dict(self):
'url': self.url,
}

def store(self, s3, filename, original_filename, data):
self.url, self.content_hash = store_image(s3, filename, False, original_filename, data)
def store(self, file_storage, filename, original_filename, data):
self.url, self.content_hash = store_image(file_storage, filename, False, original_filename, data)
return self.url is not None

class Bid(db.Model):
Expand Down
6 changes: 3 additions & 3 deletions api/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ def pick_ext(choices):
else:
return ""

def store_image(s3, filename, append_hash, original_filename, data):
def store_image(file_storage, filename, append_hash, original_filename, data):
if data is None:
url = original_filename
response = requests.get(url)
Expand All @@ -35,9 +35,9 @@ def store_image(s3, filename, append_hash, original_filename, data):
ext = pick_ext([guess_ext(data), original_filename.rsplit('.', 1)[-1]])
filename = f"{filename}_{content_hash}{ext}" if append_hash else f"{filename}{ext}"

s3.upload(data, filename)
file_storage.upload(data, filename)

return s3.get_url_prefix() + s3.get_filename_prefix() + filename, content_hash
return file_storage.get_url_prefix() + file_storage.get_filename_prefix() + filename, content_hash

def usd2sats(amount: float, btc2usd: float) -> int:
from main import app
Expand Down
1 change: 1 addition & 0 deletions docker-compose.staging.yml
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ services:
- proxy
volumes:
- "buyer-app-static-content:/buyer-app"
- "/home/www/plebeian-market-state/media:/media"
finalize-auctions:
env_file: .env.staging
volumes:
Expand Down
6 changes: 6 additions & 0 deletions install.sh
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@ echo "{\"server\": \"\", \"username\": \"\", \"password\": \"\", \"default_sende
echo "{\"NSEC\": \"\"}" > plebeian-market-secrets/nostr.json
echo "{\"NSEC\": \"\", \"XPUB\": \"\", \"LIGHTNING_ADDRESS\": \"\"}" > plebeian-market-secrets/site-admin.json

cd && mkdir -p plebeian-market-state/media

cat << EOF > .env.api
ENV=prod
FLASK_APP=main
Expand Down Expand Up @@ -108,6 +110,9 @@ server {
proxy_read_timeout 86400;
proxy_redirect off;
}
location /media {
alias /media/;
}
location / {
add_header Access-Control-Allow-Origin *;
root /buyer-app;
Expand Down Expand Up @@ -192,6 +197,7 @@ services:
volumes:
- "./plebeian-market-nginx:/etc/nginx/conf.d"
- "./plebeian-market-certificates:/cert"
- "./plebeian-market-state/media:/media"
- "buyer-app-static-content:/buyer-app"
networks:
Expand Down
4 changes: 4 additions & 0 deletions services/nginx/nginx.conf
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,10 @@ server {
proxy_redirect off;
}

location /media {
alias /media/;
}

location / {
add_header Access-Control-Allow-Origin *;
root /buyer-app;
Expand Down

0 comments on commit 9a78ad3

Please sign in to comment.