Fix UUID subscriptable error and improve Monero account creation

- Fix crypto_processor.py UUID slicing error by using shop.uuid_str
- Use clean UUID-based label: mps-shop-{uuid}
- Add account tagging with shop name for human identification
- Update development.ini to use environment variables for payment toggles
- Run black formatter on all Python files

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Russell Ballestrini 2025-09-22 08:27:09 -04:00
parent 2a89b94a29
commit 3f23b7eea8
57 changed files with 1172 additions and 797 deletions

View file

@ -65,8 +65,8 @@ app.bucket.secure_uploads.secret_key = ${MPS_APP_SECURE_UPLOADS_SECRET_KEY}
app.stripe.test_mode = True
# Payment method toggles
app.payments.stripe.enabled = True
app.payments.monero.enabled = False
app.payments.stripe.enabled = ${MPS_PAYMENTS_STRIPE_ENABLED:-True}
app.payments.monero.enabled = ${MPS_PAYMENTS_MONERO_ENABLED:-False}
# Monero RPC Configuration
# RPC endpoint of monero-wallet-rpc (recommend binding to localhost only)

View file

@ -22,6 +22,7 @@ import dkim
import smtplib
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
# Catch socket errors when postfix isn't running...
from socket import error as socket_error
import logging

View file

@ -187,7 +187,9 @@ def main():
.first()
)
if not xmr_processor or not xmr_processor.sweep_to_address:
logger.error(f"Shop {shop.name} has no Monero processor with cold wallet configured")
logger.error(
f"Shop {shop.name} has no Monero processor with cold wallet configured"
)
sys.exit(1)
shops_to_sweep = [shop]
elif args.all_shops:
@ -223,16 +225,18 @@ def main():
.filter_by(shop_id=shop.id, coin_type="XMR", enabled=True)
.first()
)
if not xmr_processor:
logger.warning(f"Shop {shop.name} has no enabled XMR processor, skipping")
continue
print(f"\n=== Sweeping shop: {shop.name} ===")
print(f"Account index: {xmr_processor.wallet_label}")
print(f"Cold wallet: {xmr_processor.sweep_to_address}")
account_index = int(xmr_processor.wallet_label) # wallet_label stores the account index
account_index = int(
xmr_processor.wallet_label
) # wallet_label stores the account index
try:
if args.dry_run:

View file

@ -279,9 +279,9 @@ class Cart(RBase, Base):
shop_total_in_cents,
) in self.shop_totals_in_cents.items():
if coupon.shop_uuid_str == shop_uuid:
self._discounted_shop_totals_in_cents[
shop_uuid
] = coupon.compute_discount(shop_total_in_cents)
self._discounted_shop_totals_in_cents[shop_uuid] = (
coupon.compute_discount(shop_total_in_cents)
)
return self._discounted_shop_totals_in_cents
@property

View file

@ -4,7 +4,14 @@ from collections import (
)
from sqlalchemy import (
BigInteger, Boolean, Column, Integer, Unicode, UnicodeText, or_, func
BigInteger,
Boolean,
Column,
Integer,
Unicode,
UnicodeText,
or_,
func,
)
from sqlalchemy.orm import relationship, backref
@ -56,25 +63,25 @@ class Comment(RBase, Base):
parent_id = Column(UUIDType, foreign_key("Comment", "id"), default=None)
product_id = Column(UUIDType, foreign_key("Product", "id"), default=None)
user_id = Column(UUIDType, foreign_key("User", "id"), default=None)
title = Column(Unicode(256), default=None)
data = Column(UnicodeText, default=None)
data_html = Column(UnicodeText, default=None)
# the depth of this comment in the thread's graph / tree.
graph_depth = Column(Integer, nullable=False, default=-1)
created_timestamp = Column(BigInteger, nullable=False)
updated_timestamp = Column(BigInteger, nullable=False)
disabled_timestamp = Column(BigInteger)
disabled = Column(Boolean, default=False)
verified = Column(Boolean, default=False)
# a locked thread (root comment) prevents new commenting.
locked = Column(Boolean, default=False)
# by default comments are approved. unless shop requires approval.
approved = Column(Boolean, default=True)
ip_address = Column(Unicode(45), default=None)
# lazy='joined' performs a left join to reduce queries, it's magic.
@ -103,7 +110,6 @@ class Comment(RBase, Base):
overlaps="children,parent",
)
def __init__(self):
self.id = uuid.uuid1()
self.created_timestamp = now_timestamp()
@ -203,54 +209,58 @@ class Comment(RBase, Base):
self.disabled_timestamp = None
self.stamp_updated_timestamp()
def has_user_purchased_product(self, user_id):
"""Check if user has purchased this product (for purchase-required commenting)."""
if not self.product:
return False
# Check if user has purchased this product
from .user_product import UserProduct
user_product = self.dbsession.query(UserProduct).filter(
UserProduct.user_id == user_id,
UserProduct.product_id == self.product_id
).first()
user_product = (
self.dbsession.query(UserProduct)
.filter(
UserProduct.user_id == user_id,
UserProduct.product_id == self.product_id,
)
.first()
)
return user_product is not None
def can_user_comment(self, user, shop):
"""Check if user can comment based on shop settings."""
if not shop.comments_enabled:
return False, "Comments are disabled for this shop"
if self.is_locked:
return False, "This comment thread is locked"
if shop.comments_require_purchase and user:
if not self.has_user_purchased_product(user.id):
return False, "You must purchase this product to leave a comment"
return True, None
def can_user_comment(self, user, shop):
"""Check if user can reply to this comment based on shop settings."""
if not shop.comments_enabled:
return False, "Comments are disabled for this shop"
if self.is_locked:
return False, "This comment thread is locked"
if shop.comments_require_purchase and user:
if not self.has_user_purchased_product(user.id):
return False, "You must purchase this product to leave a comment"
return True, None
def can_user_moderate(self, user, shop):
"""Check if user can moderate comments (approve, delete, etc)."""
if not user:
return False
# Shop owners and editors can moderate
return shop.is_owner(user) or shop.is_editor(user)
@ -265,27 +275,28 @@ def get_comments_for_product(dbsession, product_id, shop=None, user=None):
query = dbsession.query(Comment).filter(
Comment.product_id == product_id,
Comment.parent_id == None,
Comment.disabled == False
Comment.disabled == False,
)
# If shop requires approval, only show approved comments to regular users
if shop and shop.comments_require_approval:
# Shop owners/editors can see all comments
if not (user and (shop.is_owner(user) or shop.is_editor(user))):
query = query.filter(Comment.approved == True)
return query.order_by(Comment.created_timestamp.desc()).all()
def get_recent_comments(dbsession, shop_id=None, limit=10):
"""Get recent comments, optionally filtered by shop."""
query = dbsession.query(Comment).filter(Comment.approved == True)
if shop_id:
# Join with Product to filter by shop
from .product import Product
query = query.join(Product).filter(Product.shop_id == shop_id)
return query.order_by(Comment.created_timestamp.desc()).limit(limit).all()
@ -294,7 +305,7 @@ def get_total_comment_count_for_product(dbsession, product_id, shop=None, user=N
query = dbsession.query(Comment).filter(
Comment.product_id == product_id,
Comment.disabled == False,
Comment.approved == True
Comment.approved == True,
)
return query.count()
return query.count()

View file

@ -136,4 +136,3 @@ class CryptoPayment(RBase, Base):
return 0
# Return the received amount for this specific payment
return self.received_amount

View file

@ -273,9 +273,12 @@ class Invoice(RBase, Base):
try:
if hasattr(self, "crypto_payment") and self.crypto_payment:
# crypto_payment is a collection, get the first one
if hasattr(self.crypto_payment, '__len__') and len(self.crypto_payment) > 0:
if (
hasattr(self.crypto_payment, "__len__")
and len(self.crypto_payment) > 0
):
return self.crypto_payment[0].coin_type.lower()
elif hasattr(self.crypto_payment, 'coin_type'):
elif hasattr(self.crypto_payment, "coin_type"):
return self.crypto_payment.coin_type.lower()
except Exception:
# Fall back to stripe if there's any issue accessing crypto_payment

View file

@ -500,32 +500,30 @@ class Product(RBase, Base):
return "private"
else:
return "public-read"
# Product files are always private regardless of visibility
# Access is controlled via presigned URLs
return "private"
def update_s3_acls(self, s3_client, bucket_name):
"""Update S3 ACLs for all product files based on current visibility."""
# Update ACLs for all file types
for file_key in self.file_keys:
if file_key in self.extensions:
s3_key = getattr(self, f's3_key_{file_key}', None)
if s3_key is None and file_key == 'product':
s3_key = getattr(self, f"s3_key_{file_key}", None)
if s3_key is None and file_key == "product":
s3_key = self.s3_key
elif s3_key is None and file_key == 'preview':
elif s3_key is None and file_key == "preview":
s3_key = self.s3_key_preview
elif s3_key is None and file_key.startswith('thumbnail'):
elif s3_key is None and file_key.startswith("thumbnail"):
s3_key = self.s3_key_thumbnail(file_key)
if s3_key:
try:
acl = self.get_s3_acl_for_file_key(file_key)
s3_client.put_object_acl(
Bucket=bucket_name,
Key=s3_key,
ACL=acl
Bucket=bucket_name, Key=s3_key, ACL=acl
)
except Exception as e:
# Log error but don't fail the visibility change
@ -535,7 +533,7 @@ class Product(RBase, Base):
"""Set product visibility and update S3 ACLs accordingly."""
old_visibility = self.visibility
self.visibility = new_visibility
# Update S3 ACLs if client provided and visibility changed
if s3_client and bucket_name and old_visibility != new_visibility:
self.update_s3_acls(s3_client, bucket_name)
@ -543,32 +541,31 @@ class Product(RBase, Base):
def has_user_purchased_product(self, user_id):
"""Check if user has purchased this product (for purchase-required commenting)."""
# Check if user has purchased this product
user_product = self.dbsession.query(UserProduct).filter(
UserProduct.user_id == user_id,
UserProduct.product_id == self.id
).first()
user_product = (
self.dbsession.query(UserProduct)
.filter(UserProduct.user_id == user_id, UserProduct.product_id == self.id)
.first()
)
return user_product is not None
def can_user_comment(self, user, shop):
"""Check if user can comment on this product based on shop settings."""
if not shop.comments_enabled:
return False, "Comments are disabled for this shop"
if shop.comments_require_purchase and user:
if not self.has_user_purchased_product(user.id):
return False, "You must purchase this product to leave a comment"
return True, None
@property
def public_comments(self):
"""Return only approved, non-disabled comments for public display."""
from .comment import Comment
return self.comments.filter(
Comment.approved == True,
Comment.disabled == False
)
return self.comments.filter(Comment.approved == True, Comment.disabled == False)
@property
def public_comment_count(self):

View file

@ -69,7 +69,7 @@ class StripeUserShop(RBase, Base):
card = self.get_card_by_id(self.active_card_id)
if card:
return card
# If no active card or the active card doesn't exist anymore,
# automatically set the first available card as active
available_cards = self.stripe_cards
@ -78,12 +78,13 @@ class StripeUserShop(RBase, Base):
self.active_card_id = available_cards[0].id
# Save to database using object_session
from sqlalchemy.orm.session import object_session
session = object_session(self)
if session:
session.add(self)
session.flush()
return available_cards[0]
return None

View file

@ -94,7 +94,9 @@ def includeme(config):
config.add_route("shop_sales", "/s/{shop_id}/sales")
config.add_route("shop_settings", "/s/{shop_id}/settings")
config.add_route("crypto_processor_settings", "/s/{shop_id}/crypto-processor/{coin_type}")
config.add_route(
"crypto_processor_settings", "/s/{shop_id}/crypto-processor/{coin_type}"
)
config.add_route("shop_users", "/s/{shop_id}/users")
config.add_route("shop_user_remove", "/s/{shop_id}/remove-user")

View file

@ -1,4 +1,3 @@
from logging.config import fileConfig
from sqlalchemy import engine_from_config
@ -20,6 +19,7 @@ fileConfig(config.config_file_name)
# target_metadata = mymodel.Base.metadata
from make_post_sell.models import Base
target_metadata = Base.metadata
# other values from the config, defined by the needs of env.py,
@ -41,9 +41,7 @@ def run_migrations_offline():
"""
url = config.get_main_option("sqlalchemy.url")
context.configure(
url=url, target_metadata=target_metadata, literal_binds=True
)
context.configure(url=url, target_metadata=target_metadata, literal_binds=True)
with context.begin_transaction():
context.run_migrations()
@ -63,9 +61,7 @@ def run_migrations_online():
)
with connectable.connect() as connection:
context.configure(
connection=connection, target_metadata=target_metadata
)
context.configure(connection=connection, target_metadata=target_metadata)
with context.begin_transaction():
context.run_migrations()

View file

@ -5,13 +5,14 @@ Revises: e6ac162d096f
Create Date: 2022-06-27 12:31:50.885273
"""
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision = '03292288a132'
down_revision = 'e6ac162d096f'
revision = "03292288a132"
down_revision = "e6ac162d096f"
branch_labels = None
depends_on = None
@ -20,11 +21,14 @@ from make_post_sell.models.meta import UUIDType
def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.add_column('mps_user_shop', sa.Column('role_id', sa.Integer(), nullable=False, server_default="0"))
op.add_column(
"mps_user_shop",
sa.Column("role_id", sa.Integer(), nullable=False, server_default="0"),
)
# ### end Alembic commands ###
def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.drop_column('mps_user_shop', 'role_id')
op.drop_column("mps_user_shop", "role_id")
# ### end Alembic commands ###

View file

@ -5,20 +5,29 @@ Revises: 201924e6306f
Create Date: 2022-07-27 14:50:19.889862
"""
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision = '1275752bc491'
down_revision = '201924e6306f'
revision = "1275752bc491"
down_revision = "201924e6306f"
branch_labels = None
depends_on = None
def upgrade():
op.add_column('mps_shop', sa.Column('updated_timestamp', sa.BigInteger(), nullable=False, server_default="1657814552131"))
op.add_column(
"mps_shop",
sa.Column(
"updated_timestamp",
sa.BigInteger(),
nullable=False,
server_default="1657814552131",
),
)
def downgrade():
op.drop_column('mps_shop', 'updated_timestamp')
op.drop_column("mps_shop", "updated_timestamp")

View file

@ -5,24 +5,33 @@ Revises: b0d5604d9255
Create Date: 2019-08-11 15:53:30.385027
"""
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision = '15751f5d7a03'
down_revision = 'b0d5604d9255'
revision = "15751f5d7a03"
down_revision = "b0d5604d9255"
branch_labels = None
depends_on = None
def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.add_column('mps_user', sa.Column('active_cart_total_in_cents', sa.BigInteger(), nullable=False, server_default="0"))
op.add_column(
"mps_user",
sa.Column(
"active_cart_total_in_cents",
sa.BigInteger(),
nullable=False,
server_default="0",
),
)
# ### end Alembic commands ###
def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.drop_column('mps_user', 'active_cart_total_in_cents')
op.drop_column("mps_user", "active_cart_total_in_cents")
# ### end Alembic commands ###

View file

@ -5,13 +5,14 @@ Revises: 81d65d8605c2
Create Date: 2025-09-20 21:31:26.257478
"""
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision = '193438acaa95'
down_revision = '81d65d8605c2'
revision = "193438acaa95"
down_revision = "81d65d8605c2"
branch_labels = None
depends_on = None
@ -20,8 +21,16 @@ from make_post_sell.models.meta import UUIDType
def upgrade():
# Add crypto quote expiry time column with default of 3600 seconds (60 minutes)
op.add_column('mps_shop', sa.Column('crypto_quote_expiry_seconds', sa.BigInteger(), nullable=False, server_default='3600'))
op.add_column(
"mps_shop",
sa.Column(
"crypto_quote_expiry_seconds",
sa.BigInteger(),
nullable=False,
server_default="3600",
),
)
def downgrade():
op.drop_column('mps_shop', 'crypto_quote_expiry_seconds')
op.drop_column("mps_shop", "crypto_quote_expiry_seconds")

View file

@ -5,13 +5,14 @@ Revises: ddfbd6d96915
Create Date: 2024-06-16 14:30:57.649239
"""
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision = '1b3ecdde9e65'
down_revision = 'ddfbd6d96915'
revision = "1b3ecdde9e65"
down_revision = "ddfbd6d96915"
branch_labels = None
depends_on = None
@ -20,17 +21,27 @@ from make_post_sell.models.meta import UUIDType
def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.add_column('mps_cart', sa.Column('handling_option', sa.Unicode(length=64), nullable=True))
op.add_column('mps_cart', sa.Column('handling_cost_in_cents', sa.BigInteger(), nullable=True))
op.add_column('mps_invoice', sa.Column('handling_option', sa.Unicode(length=64), nullable=True))
op.add_column('mps_invoice', sa.Column('handling_cost_in_cents', sa.BigInteger(), nullable=True))
op.add_column(
"mps_cart", sa.Column("handling_option", sa.Unicode(length=64), nullable=True)
)
op.add_column(
"mps_cart", sa.Column("handling_cost_in_cents", sa.BigInteger(), nullable=True)
)
op.add_column(
"mps_invoice",
sa.Column("handling_option", sa.Unicode(length=64), nullable=True),
)
op.add_column(
"mps_invoice",
sa.Column("handling_cost_in_cents", sa.BigInteger(), nullable=True),
)
# ### end Alembic commands ###
def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.drop_column('mps_invoice', 'handling_cost_in_cents')
op.drop_column('mps_invoice', 'handling_option')
op.drop_column('mps_cart', 'handling_cost_in_cents')
op.drop_column('mps_cart', 'handling_option')
op.drop_column("mps_invoice", "handling_cost_in_cents")
op.drop_column("mps_invoice", "handling_option")
op.drop_column("mps_cart", "handling_cost_in_cents")
op.drop_column("mps_cart", "handling_option")
# ### end Alembic commands ###

View file

@ -5,13 +5,14 @@ Revises: aad3d454a6f4
Create Date: 2022-07-10 13:21:40.740356
"""
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision = '201924e6306f'
down_revision = 'aad3d454a6f4'
revision = "201924e6306f"
down_revision = "aad3d454a6f4"
branch_labels = None
depends_on = None
@ -20,18 +21,18 @@ from make_post_sell.models.meta import UUIDType
def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.add_column('mps_cart', sa.Column('shop_id', UUIDType, nullable=True))
op.add_column('mps_cart', sa.Column('active', sa.Boolean(), nullable=True))
op.create_index(op.f('ix_mps_cart_id'), 'mps_cart', ['id'], unique=False)
#op.create_foreign_key(None, 'mps_cart', 'mps_user', ['user_id'], ['id'])
#op.create_foreign_key(None, 'mps_cart', 'mps_shop', ['shop_id'], ['id'])
#op.drop_column('mps_user_shop', 'default')
op.add_column("mps_cart", sa.Column("shop_id", UUIDType, nullable=True))
op.add_column("mps_cart", sa.Column("active", sa.Boolean(), nullable=True))
op.create_index(op.f("ix_mps_cart_id"), "mps_cart", ["id"], unique=False)
# op.create_foreign_key(None, 'mps_cart', 'mps_user', ['user_id'], ['id'])
# op.create_foreign_key(None, 'mps_cart', 'mps_shop', ['shop_id'], ['id'])
# op.drop_column('mps_user_shop', 'default')
# ### end Alembic commands ###
def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.drop_index(op.f('ix_mps_cart_id'), table_name='mps_cart')
op.drop_column('mps_cart', 'active')
op.drop_column('mps_cart', 'shop_id')
op.drop_index(op.f("ix_mps_cart_id"), table_name="mps_cart")
op.drop_column("mps_cart", "active")
op.drop_column("mps_cart", "shop_id")
# ### end Alembic commands ###

View file

@ -5,13 +5,14 @@ Revises: 3e1e70bfe89d
Create Date: 2020-02-03 00:03:11.599838
"""
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision = '24662905655f'
down_revision = '3e1e70bfe89d'
revision = "24662905655f"
down_revision = "3e1e70bfe89d"
branch_labels = None
depends_on = None
@ -19,8 +20,8 @@ from make_post_sell.models.meta import UUIDType
def upgrade():
op.add_column('mps_user', sa.Column('active_shop_id', UUIDType, nullable=True))
op.add_column("mps_user", sa.Column("active_shop_id", UUIDType, nullable=True))
def downgrade():
op.drop_column('mps_user', 'active_shop_id')
op.drop_column("mps_user", "active_shop_id")

View file

@ -5,20 +5,24 @@ Revises: eadd0d69e133
Create Date: 2019-11-20 11:06:28.092483
"""
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision = '3e1e70bfe89d'
down_revision = 'eadd0d69e133'
revision = "3e1e70bfe89d"
down_revision = "eadd0d69e133"
branch_labels = None
depends_on = None
def upgrade():
op.add_column('mps_shop', sa.Column('google_analytics_id', sa.Unicode(length=32), nullable=True))
op.add_column(
"mps_shop",
sa.Column("google_analytics_id", sa.Unicode(length=32), nullable=True),
)
def downgrade():
op.drop_column('mps_shop', 'google_analytics_id')
op.drop_column("mps_shop", "google_analytics_id")

View file

@ -6,13 +6,14 @@ Revises: af28cc35ed6f
Create Date: 2021-10-27 09:00:32.238571
"""
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision = '4d9da43b0e86'
down_revision = 'af28cc35ed6f'
revision = "4d9da43b0e86"
down_revision = "af28cc35ed6f"
branch_labels = None
depends_on = None
@ -20,8 +21,11 @@ from make_post_sell.models.meta import UUIDType
def upgrade():
op.add_column('mps_product', sa.Column('visibility', sa.Integer(), nullable=False, server_default="1"))
op.add_column(
"mps_product",
sa.Column("visibility", sa.Integer(), nullable=False, server_default="1"),
)
def downgrade():
op.drop_column('mps_product', 'visibility')
op.drop_column("mps_product", "visibility")

View file

@ -5,24 +5,29 @@ Revises: f882f2255cd1
Create Date: 2019-10-29 11:59:01.050032
"""
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision = '5c40dfb56179'
down_revision = 'f882f2255cd1'
revision = "5c40dfb56179"
down_revision = "f882f2255cd1"
branch_labels = None
depends_on = None
def upgrade():
op.add_column('mps_shop', sa.Column('ribbon_color_1', sa.Unicode(length=32), nullable=True))
op.add_column('mps_shop', sa.Column('ribbon_color_2', sa.Unicode(length=32), nullable=True))
op.add_column('mps_shop', sa.Column('ribbon_text', sa.UnicodeText(), nullable=True))
op.add_column(
"mps_shop", sa.Column("ribbon_color_1", sa.Unicode(length=32), nullable=True)
)
op.add_column(
"mps_shop", sa.Column("ribbon_color_2", sa.Unicode(length=32), nullable=True)
)
op.add_column("mps_shop", sa.Column("ribbon_text", sa.UnicodeText(), nullable=True))
def downgrade():
op.drop_column('mps_shop', 'ribbon_text')
op.drop_column('mps_shop', 'ribbon_color_2')
op.drop_column('mps_shop', 'ribbon_color_1')
op.drop_column("mps_shop", "ribbon_text")
op.drop_column("mps_shop", "ribbon_color_2")
op.drop_column("mps_shop", "ribbon_color_1")

View file

@ -5,24 +5,30 @@ Revises: 15751f5d7a03
Create Date: 2019-08-13 08:52:18.073705
"""
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision = '6da97f2f913f'
down_revision = '15751f5d7a03'
revision = "6da97f2f913f"
down_revision = "15751f5d7a03"
branch_labels = None
depends_on = None
def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.add_column('mps_cart', sa.Column('updated_timestamp', sa.BigInteger(), nullable=False, server_default="0"))
op.add_column(
"mps_cart",
sa.Column(
"updated_timestamp", sa.BigInteger(), nullable=False, server_default="0"
),
)
# ### end Alembic commands ###
def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.drop_column('mps_cart', 'updated_timestamp')
op.drop_column("mps_cart", "updated_timestamp")
# ### end Alembic commands ###

View file

@ -5,13 +5,14 @@ Revises: d38d649cb682
Create Date: 2022-09-13 14:57:57.100700
"""
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision = '7217f39a20a8'
down_revision = 'd38d649cb682'
revision = "7217f39a20a8"
down_revision = "d38d649cb682"
branch_labels = None
depends_on = None
@ -19,10 +20,20 @@ from make_post_sell.models.meta import UUIDType
def upgrade():
op.add_column('mps_product', sa.Column('json_file_bytes', sa.UnicodeText(), nullable=True, server_default="{}"))
op.add_column('mps_product', sa.Column('total_file_bytes', sa.BigInteger(), nullable=False, server_default="0"))
op.add_column(
"mps_product",
sa.Column(
"json_file_bytes", sa.UnicodeText(), nullable=True, server_default="{}"
),
)
op.add_column(
"mps_product",
sa.Column(
"total_file_bytes", sa.BigInteger(), nullable=False, server_default="0"
),
)
def downgrade():
op.drop_column('mps_product', 'total_file_bytes')
op.drop_column('mps_product', 'json_file_bytes')
op.drop_column("mps_product", "total_file_bytes")
op.drop_column("mps_product", "json_file_bytes")

View file

@ -5,13 +5,14 @@ Revises: 1b3ecdde9e65, fd9f7e2f2b78
Create Date: 2025-06-24 08:09:33.656088
"""
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision = '81d65d8605c2'
down_revision = ('1b3ecdde9e65', 'fd9f7e2f2b78')
revision = "81d65d8605c2"
down_revision = ("1b3ecdde9e65", "fd9f7e2f2b78")
branch_labels = None
depends_on = None
@ -23,30 +24,51 @@ def upgrade():
import sqlalchemy as sa
from alembic import op
from sqlalchemy import text
# Check if columns exist before adding them
conn = op.get_bind()
# Check if comments_enabled exists
try:
conn.execute(text("SELECT comments_enabled FROM mps_shop LIMIT 1"))
except:
op.add_column('mps_shop', sa.Column('comments_enabled', sa.Boolean(), nullable=True, server_default="1"))
# Check if comments_require_purchase exists
op.add_column(
"mps_shop",
sa.Column(
"comments_enabled", sa.Boolean(), nullable=True, server_default="1"
),
)
# Check if comments_require_purchase exists
try:
conn.execute(text("SELECT comments_require_purchase FROM mps_shop LIMIT 1"))
except:
op.add_column('mps_shop', sa.Column('comments_require_purchase', sa.Boolean(), nullable=True, server_default="0"))
op.add_column(
"mps_shop",
sa.Column(
"comments_require_purchase",
sa.Boolean(),
nullable=True,
server_default="0",
),
)
# Check if comments_require_approval exists
try:
conn.execute(text("SELECT comments_require_approval FROM mps_shop LIMIT 1"))
except:
op.add_column('mps_shop', sa.Column('comments_require_approval', sa.Boolean(), nullable=True, server_default="0"))
op.add_column(
"mps_shop",
sa.Column(
"comments_require_approval",
sa.Boolean(),
nullable=True,
server_default="0",
),
)
def downgrade():
op.drop_column('mps_shop', 'comments_require_approval')
op.drop_column('mps_shop', 'comments_require_purchase')
op.drop_column('mps_shop', 'comments_enabled')
op.drop_column("mps_shop", "comments_require_approval")
op.drop_column("mps_shop", "comments_require_purchase")
op.drop_column("mps_shop", "comments_enabled")

View file

@ -5,26 +5,47 @@ Revises: 24662905655f
Create Date: 2020-03-21 18:34:12.244626
"""
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision = '96f851cbab01'
down_revision = '24662905655f'
revision = "96f851cbab01"
down_revision = "24662905655f"
branch_labels = None
depends_on = None
def upgrade():
op.add_column('mps_shop', sa.Column('privacy_policy_html', sa.UnicodeText(), nullable=True, server_default=""))
op.add_column('mps_shop', sa.Column('privacy_policy_raw', sa.UnicodeText(), nullable=True, server_default=""))
op.add_column('mps_shop', sa.Column('terms_of_service_html', sa.UnicodeText(), nullable=True, server_default=""))
op.add_column('mps_shop', sa.Column('terms_of_service_raw', sa.UnicodeText(), nullable=True, server_default=""))
op.add_column(
"mps_shop",
sa.Column(
"privacy_policy_html", sa.UnicodeText(), nullable=True, server_default=""
),
)
op.add_column(
"mps_shop",
sa.Column(
"privacy_policy_raw", sa.UnicodeText(), nullable=True, server_default=""
),
)
op.add_column(
"mps_shop",
sa.Column(
"terms_of_service_html", sa.UnicodeText(), nullable=True, server_default=""
),
)
op.add_column(
"mps_shop",
sa.Column(
"terms_of_service_raw", sa.UnicodeText(), nullable=True, server_default=""
),
)
def downgrade():
op.drop_column('mps_shop', 'terms_of_service_raw')
op.drop_column('mps_shop', 'terms_of_service_html')
op.drop_column('mps_shop', 'privacy_policy_raw')
op.drop_column('mps_shop', 'privacy_policy_html')
op.drop_column("mps_shop", "terms_of_service_raw")
op.drop_column("mps_shop", "terms_of_service_html")
op.drop_column("mps_shop", "privacy_policy_raw")
op.drop_column("mps_shop", "privacy_policy_html")

View file

@ -5,13 +5,14 @@ Revises: 03292288a132
Create Date: 2022-06-30 16:48:22.471896
"""
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision = 'aad3d454a6f4'
down_revision = '03292288a132'
revision = "aad3d454a6f4"
down_revision = "03292288a132"
branch_labels = None
depends_on = None
@ -20,11 +21,14 @@ from make_post_sell.models.meta import UUIDType
def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.add_column('mps_shop', sa.Column('logo_banner', sa.Boolean(), nullable=True, server_default="1"))
op.add_column(
"mps_shop",
sa.Column("logo_banner", sa.Boolean(), nullable=True, server_default="1"),
)
# ### end Alembic commands ###
def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.drop_column('mps_shop', 'logo_banner')
op.drop_column("mps_shop", "logo_banner")
# ### end Alembic commands ###

View file

@ -5,19 +5,25 @@ Revises: fd9f7e2f2b78
Create Date: 2019-08-10 11:20:43.838087
"""
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision = 'ab7338eaedf1'
down_revision = 'fd9f7e2f2b78'
revision = "ab7338eaedf1"
down_revision = "fd9f7e2f2b78"
branch_labels = None
depends_on = None
def upgrade():
op.add_column('mps_product', sa.Column('price_in_cents', sa.BigInteger(), nullable=False, server_default="3.50"))
op.add_column(
"mps_product",
sa.Column(
"price_in_cents", sa.BigInteger(), nullable=False, server_default="3.50"
),
)
def downgrade():

View file

@ -5,13 +5,14 @@ Revises: c4d5566ec87d
Create Date: 2021-04-15 09:30:53.107239
"""
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision = 'af28cc35ed6f'
down_revision = 'c4d5566ec87d'
revision = "af28cc35ed6f"
down_revision = "c4d5566ec87d"
branch_labels = None
depends_on = None
@ -22,13 +23,24 @@ from make_post_sell.models.product import DEFAULT_BUNDLE_METADATA
def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.add_column('mps_product', sa.Column('is_bundle', sa.Boolean(), nullable=False, server_default="0"))
op.add_column('mps_product', sa.Column('json_bundle_metadata', sa.UnicodeText(), nullable=False, server_default=DEFAULT_BUNDLE_METADATA))
op.add_column(
"mps_product",
sa.Column("is_bundle", sa.Boolean(), nullable=False, server_default="0"),
)
op.add_column(
"mps_product",
sa.Column(
"json_bundle_metadata",
sa.UnicodeText(),
nullable=False,
server_default=DEFAULT_BUNDLE_METADATA,
),
)
# ### end Alembic commands ###
def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.drop_column('mps_product', 'json_bundle_metadata')
op.drop_column('mps_product', 'is_bundle')
op.drop_column("mps_product", "json_bundle_metadata")
op.drop_column("mps_product", "is_bundle")
# ### end Alembic commands ###

View file

@ -5,24 +5,30 @@ Revises: ab7338eaedf1
Create Date: 2019-08-10 20:15:45.595633
"""
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision = 'b0d5604d9255'
down_revision = 'ab7338eaedf1'
revision = "b0d5604d9255"
down_revision = "ab7338eaedf1"
branch_labels = None
depends_on = None
def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.add_column('mps_user', sa.Column('active_cart_count', sa.Integer(), server_default="0", nullable=False))
op.add_column(
"mps_user",
sa.Column(
"active_cart_count", sa.Integer(), server_default="0", nullable=False
),
)
# ### end Alembic commands ###
def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.drop_column('mps_user', 'active_cart_count')
op.drop_column("mps_user", "active_cart_count")
# ### end Alembic commands ###

View file

@ -5,13 +5,14 @@ Revises: 1275752bc491
Create Date: 2022-08-03 11:53:34.191328
"""
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision = 'b8be6cd51cbd'
down_revision = '1275752bc491'
revision = "b8be6cd51cbd"
down_revision = "1275752bc491"
branch_labels = None
depends_on = None
@ -19,9 +20,11 @@ from make_post_sell.models.meta import UUIDType
def upgrade():
op.add_column('mps_stripe_user_shop', sa.Column('active_card_id', sa.Unicode(length=64), nullable=True))
op.add_column(
"mps_stripe_user_shop",
sa.Column("active_card_id", sa.Unicode(length=64), nullable=True),
)
def downgrade():
op.drop_column('mps_stripe_user_shop', 'active_card_id')
op.drop_column("mps_stripe_user_shop", "active_card_id")

View file

@ -5,13 +5,14 @@ Revises: 4d9da43b0e86
Create Date: 2021-12-02 17:32:08.498999
"""
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision = 'bfc4195a7f56'
down_revision = '4d9da43b0e86'
revision = "bfc4195a7f56"
down_revision = "4d9da43b0e86"
branch_labels = None
depends_on = None
@ -19,8 +20,11 @@ from make_post_sell.models.meta import UUIDType
def upgrade():
op.add_column('mps_product', sa.Column('is_sellable', sa.Boolean(), nullable=False, server_default="1"))
op.add_column(
"mps_product",
sa.Column("is_sellable", sa.Boolean(), nullable=False, server_default="1"),
)
def downgrade():
op.drop_column('mps_product', 'is_sellable')
op.drop_column("mps_product", "is_sellable")

View file

@ -5,13 +5,14 @@ Revises: cdad5c220c39
Create Date: 2022-01-31 15:45:34.314442
"""
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision = 'c263b4fba9e7'
down_revision = 'cdad5c220c39'
revision = "c263b4fba9e7"
down_revision = "cdad5c220c39"
branch_labels = None
depends_on = None
@ -19,12 +20,21 @@ from make_post_sell.models.meta import UUIDType
def upgrade():
op.add_column('mps_shop', sa.Column('maint_mode', sa.Boolean(), nullable=True, server_default="0"))
op.add_column('mps_shop', sa.Column('favicon', sa.Boolean(), nullable=True, server_default="0"))
op.add_column('mps_user', sa.Column('theme_id', sa.BigInteger(), nullable=False, server_default="1"))
op.add_column(
"mps_shop",
sa.Column("maint_mode", sa.Boolean(), nullable=True, server_default="0"),
)
op.add_column(
"mps_shop",
sa.Column("favicon", sa.Boolean(), nullable=True, server_default="0"),
)
op.add_column(
"mps_user",
sa.Column("theme_id", sa.BigInteger(), nullable=False, server_default="1"),
)
def downgrade():
op.drop_column('mps_shop', 'maint_mode')
op.drop_column('mps_shop', 'favicon')
op.drop_column('mps_user', 'theme_id')
op.drop_column("mps_shop", "maint_mode")
op.drop_column("mps_shop", "favicon")
op.drop_column("mps_user", "theme_id")

View file

@ -5,13 +5,14 @@ Revises: e12bbe39d87e
Create Date: 2021-01-16 11:18:10.302974
"""
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision = 'c4d5566ec87d'
down_revision = 'e12bbe39d87e'
revision = "c4d5566ec87d"
down_revision = "e12bbe39d87e"
branch_labels = None
depends_on = None
@ -19,24 +20,47 @@ from make_post_sell.models.meta import UUIDType
def upgrade():
op.create_table('mps_coupon_redemption',
sa.Column('id', UUIDType, nullable=False),
sa.Column('coupon_id', UUIDType, nullable=False),
sa.Column('invoice_id', UUIDType, nullable=False),
sa.Column('shop_id', UUIDType, nullable=False),
sa.Column('user_id', UUIDType, nullable=False),
sa.Column('created_timestamp', sa.BigInteger(), nullable=False),
sa.ForeignKeyConstraint(['coupon_id'], ['mps_coupon.id'], ),
sa.ForeignKeyConstraint(['invoice_id'], ['mps_invoice.id'], ),
sa.ForeignKeyConstraint(['shop_id'], ['mps_shop.id'], ),
sa.ForeignKeyConstraint(['user_id'], ['mps_user.id'], ),
sa.PrimaryKeyConstraint('id')
op.create_table(
"mps_coupon_redemption",
sa.Column("id", UUIDType, nullable=False),
sa.Column("coupon_id", UUIDType, nullable=False),
sa.Column("invoice_id", UUIDType, nullable=False),
sa.Column("shop_id", UUIDType, nullable=False),
sa.Column("user_id", UUIDType, nullable=False),
sa.Column("created_timestamp", sa.BigInteger(), nullable=False),
sa.ForeignKeyConstraint(
["coupon_id"],
["mps_coupon.id"],
),
sa.ForeignKeyConstraint(
["invoice_id"],
["mps_invoice.id"],
),
sa.ForeignKeyConstraint(
["shop_id"],
["mps_shop.id"],
),
sa.ForeignKeyConstraint(
["user_id"],
["mps_user.id"],
),
sa.PrimaryKeyConstraint("id"),
)
op.create_index(
op.f("ix_mps_coupon_redemption_id"),
"mps_coupon_redemption",
["id"],
unique=True,
)
op.add_column(
"mps_coupon",
sa.Column("stackable", sa.Boolean(), nullable=False, server_default="0"),
)
op.create_index(op.f('ix_mps_coupon_redemption_id'), 'mps_coupon_redemption', ['id'], unique=True)
op.add_column(u'mps_coupon', sa.Column('stackable', sa.Boolean(), nullable=False, server_default="0"))
def downgrade():
op.drop_column(u'mps_coupon', 'stackable')
op.drop_index(op.f('ix_mps_coupon_redemption_id'), table_name='mps_coupon_redemption')
op.drop_table('mps_coupon_redemption')
op.drop_column("mps_coupon", "stackable")
op.drop_index(
op.f("ix_mps_coupon_redemption_id"), table_name="mps_coupon_redemption"
)
op.drop_table("mps_coupon_redemption")

View file

@ -5,13 +5,14 @@ Revises: 7217f39a20a8
Create Date: 2024-06-08 09:40:24.200194
"""
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision = 'cc6211ec360e'
down_revision = '7217f39a20a8'
revision = "cc6211ec360e"
down_revision = "7217f39a20a8"
branch_labels = None
depends_on = None
@ -20,42 +21,60 @@ from make_post_sell.models.meta import UUIDType
def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.create_table('mps_shop_location',
sa.Column('id', UUIDType, nullable=False),
sa.Column('shop_id', UUIDType, nullable=False),
sa.Column('name', sa.Unicode(length=64), nullable=False),
sa.Column('address', sa.Unicode(length=256), nullable=False),
sa.Column('city', sa.Unicode(length=64), nullable=False),
sa.Column('state', sa.Unicode(length=64), nullable=False),
sa.Column('country', sa.Unicode(length=64), nullable=False),
sa.Column('postal_code', sa.Unicode(length=16), nullable=False),
sa.Column('created_timestamp', sa.BigInteger(), nullable=False),
sa.ForeignKeyConstraint(['shop_id'], ['mps_shop.id'], ),
sa.PrimaryKeyConstraint('id')
op.create_table(
"mps_shop_location",
sa.Column("id", UUIDType, nullable=False),
sa.Column("shop_id", UUIDType, nullable=False),
sa.Column("name", sa.Unicode(length=64), nullable=False),
sa.Column("address", sa.Unicode(length=256), nullable=False),
sa.Column("city", sa.Unicode(length=64), nullable=False),
sa.Column("state", sa.Unicode(length=64), nullable=False),
sa.Column("country", sa.Unicode(length=64), nullable=False),
sa.Column("postal_code", sa.Unicode(length=16), nullable=False),
sa.Column("created_timestamp", sa.BigInteger(), nullable=False),
sa.ForeignKeyConstraint(
["shop_id"],
["mps_shop.id"],
),
sa.PrimaryKeyConstraint("id"),
)
op.create_index(op.f('ix_mps_shop_location_id'), 'mps_shop_location', ['id'], unique=False)
op.create_table('mps_inventory',
sa.Column('id', UUIDType, nullable=False),
sa.Column('shop_location_id', UUIDType, nullable=False),
sa.Column('product_id', UUIDType, nullable=False),
sa.Column('quantity', sa.Integer(), nullable=False),
sa.Column('created_timestamp', sa.BigInteger(), nullable=False),
sa.Column('updated_timestamp', sa.BigInteger(), nullable=False),
sa.ForeignKeyConstraint(['product_id'], ['mps_product.id'], ),
sa.ForeignKeyConstraint(['shop_location_id'], ['mps_shop_location.id'], ),
sa.PrimaryKeyConstraint('id')
op.create_index(
op.f("ix_mps_shop_location_id"), "mps_shop_location", ["id"], unique=False
)
op.create_index(op.f('ix_mps_inventory_id'), 'mps_inventory', ['id'], unique=False)
op.create_table(
"mps_inventory",
sa.Column("id", UUIDType, nullable=False),
sa.Column("shop_location_id", UUIDType, nullable=False),
sa.Column("product_id", UUIDType, nullable=False),
sa.Column("quantity", sa.Integer(), nullable=False),
sa.Column("created_timestamp", sa.BigInteger(), nullable=False),
sa.Column("updated_timestamp", sa.BigInteger(), nullable=False),
sa.ForeignKeyConstraint(
["product_id"],
["mps_product.id"],
),
sa.ForeignKeyConstraint(
["shop_location_id"],
["mps_shop_location.id"],
),
sa.PrimaryKeyConstraint("id"),
)
op.create_index(op.f("ix_mps_inventory_id"), "mps_inventory", ["id"], unique=False)
op.add_column('mps_product', sa.Column('is_physical', sa.Boolean(), nullable=False, server_default=sa.false()))
op.add_column(
"mps_product",
sa.Column(
"is_physical", sa.Boolean(), nullable=False, server_default=sa.false()
),
)
# ### end Alembic commands ###
def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.drop_column('mps_product', 'is_physical')
op.drop_index(op.f('ix_mps_inventory_id'), table_name='mps_inventory')
op.drop_table('mps_inventory')
op.drop_index(op.f('ix_mps_shop_location_id'), table_name='mps_shop_location')
op.drop_table('mps_shop_location')
op.drop_column("mps_product", "is_physical")
op.drop_index(op.f("ix_mps_inventory_id"), table_name="mps_inventory")
op.drop_table("mps_inventory")
op.drop_index(op.f("ix_mps_shop_location_id"), table_name="mps_shop_location")
op.drop_table("mps_shop_location")
# ### end Alembic commands ###

View file

@ -5,20 +5,29 @@ Revises: bfc4195a7f56
Create Date: 2022-01-07 19:57:52.202252
"""
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision = 'cdad5c220c39'
down_revision = 'bfc4195a7f56'
revision = "cdad5c220c39"
down_revision = "bfc4195a7f56"
branch_labels = None
depends_on = None
def upgrade():
op.add_column('mps_shop', sa.Column('plausible_domain_name', sa.Unicode(length=256), nullable=True, server_default=""))
op.add_column(
"mps_shop",
sa.Column(
"plausible_domain_name",
sa.Unicode(length=256),
nullable=True,
server_default="",
),
)
def downgrade():
op.drop_column('mps_shop', 'plausible_domain_name')
op.drop_column("mps_shop", "plausible_domain_name")

View file

@ -5,13 +5,14 @@ Revises: b8be6cd51cbd
Create Date: 2022-09-03 12:46:49.118477
"""
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision = 'd38d649cb682'
down_revision = 'b8be6cd51cbd'
revision = "d38d649cb682"
down_revision = "b8be6cd51cbd"
branch_labels = None
depends_on = None
@ -20,12 +21,14 @@ from make_post_sell.models.meta import UUIDType
def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.add_column('mps_invoice', sa.Column('delivery_address', sa.UnicodeText(), nullable=True))
op.add_column('mps_user', sa.Column('active_address_id', UUIDType, nullable=True))
op.add_column(
"mps_invoice", sa.Column("delivery_address", sa.UnicodeText(), nullable=True)
)
op.add_column("mps_user", sa.Column("active_address_id", UUIDType, nullable=True))
def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.drop_column('mps_user', 'active_address_id')
op.drop_column('mps_invoice', 'delivery_address')
op.drop_column("mps_user", "active_address_id")
op.drop_column("mps_invoice", "delivery_address")
# ### end Alembic commands ###

View file

@ -5,13 +5,14 @@ Revises: cc6211ec360e
Create Date: 2024-06-08 11:38:38.812584
"""
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision = 'ddfbd6d96915'
down_revision = 'cc6211ec360e'
revision = "ddfbd6d96915"
down_revision = "cc6211ec360e"
branch_labels = None
depends_on = None
@ -20,27 +21,67 @@ from make_post_sell.models.meta import UUIDType
def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.add_column('mps_shop_location', sa.Column('local_pickup', sa.Boolean(), nullable=False, server_default=sa.false()))
op.add_column('mps_shop_location', sa.Column('local_delivery', sa.Boolean(), nullable=False, server_default=sa.false()))
op.add_column('mps_shop_location', sa.Column('local_shipping', sa.Boolean(), nullable=False, server_default=sa.false()))
op.add_column('mps_shop_location', sa.Column('international_shipping', sa.Boolean(), nullable=False, server_default=sa.false()))
op.add_column('mps_shop_location', sa.Column('local_delivery_rate_in_cents', sa.BigInteger(), nullable=True))
op.add_column('mps_shop_location', sa.Column('local_shipping_rate_in_cents', sa.BigInteger(), nullable=True))
op.add_column('mps_shop_location', sa.Column('international_shipping_rate_in_cents', sa.BigInteger(), nullable=True))
op.add_column('mps_shop_location', sa.Column('days_open', sa.Unicode(length=64), nullable=True))
op.add_column('mps_shop_location', sa.Column('hours_open', sa.Unicode(length=64), nullable=True))
op.add_column(
"mps_shop_location",
sa.Column(
"local_pickup", sa.Boolean(), nullable=False, server_default=sa.false()
),
)
op.add_column(
"mps_shop_location",
sa.Column(
"local_delivery", sa.Boolean(), nullable=False, server_default=sa.false()
),
)
op.add_column(
"mps_shop_location",
sa.Column(
"local_shipping", sa.Boolean(), nullable=False, server_default=sa.false()
),
)
op.add_column(
"mps_shop_location",
sa.Column(
"international_shipping",
sa.Boolean(),
nullable=False,
server_default=sa.false(),
),
)
op.add_column(
"mps_shop_location",
sa.Column("local_delivery_rate_in_cents", sa.BigInteger(), nullable=True),
)
op.add_column(
"mps_shop_location",
sa.Column("local_shipping_rate_in_cents", sa.BigInteger(), nullable=True),
)
op.add_column(
"mps_shop_location",
sa.Column(
"international_shipping_rate_in_cents", sa.BigInteger(), nullable=True
),
)
op.add_column(
"mps_shop_location",
sa.Column("days_open", sa.Unicode(length=64), nullable=True),
)
op.add_column(
"mps_shop_location",
sa.Column("hours_open", sa.Unicode(length=64), nullable=True),
)
# ### end Alembic commands ###
def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.drop_column('mps_shop_location', 'hours_open')
op.drop_column('mps_shop_location', 'days_open')
op.drop_column('mps_shop_location', 'international_shipping_rate_in_cents')
op.drop_column('mps_shop_location', 'local_shipping_rate_in_cents')
op.drop_column('mps_shop_location', 'local_delivery_rate_in_cents')
op.drop_column('mps_shop_location', 'international_shipping')
op.drop_column('mps_shop_location', 'local_shipping')
op.drop_column('mps_shop_location', 'local_delivery')
op.drop_column('mps_shop_location', 'local_pickup')
op.drop_column("mps_shop_location", "hours_open")
op.drop_column("mps_shop_location", "days_open")
op.drop_column("mps_shop_location", "international_shipping_rate_in_cents")
op.drop_column("mps_shop_location", "local_shipping_rate_in_cents")
op.drop_column("mps_shop_location", "local_delivery_rate_in_cents")
op.drop_column("mps_shop_location", "international_shipping")
op.drop_column("mps_shop_location", "local_shipping")
op.drop_column("mps_shop_location", "local_delivery")
op.drop_column("mps_shop_location", "local_pickup")
# ### end Alembic commands ###

View file

@ -5,13 +5,14 @@ Revises: f3aa57777025
Create Date: 2020-08-23 12:48:47.259833
"""
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision = 'e12bbe39d87e'
down_revision = 'f3aa57777025'
revision = "e12bbe39d87e"
down_revision = "f3aa57777025"
branch_labels = None
depends_on = None
@ -20,21 +21,30 @@ from make_post_sell.models.meta import UUIDType
def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.create_table('mps_cart_coupon',
sa.Column('id', UUIDType, nullable=False),
sa.Column('cart_id', UUIDType, nullable=False),
sa.Column('coupon_id', UUIDType, nullable=False),
sa.Column('created_timestamp', sa.BigInteger(), nullable=False),
sa.ForeignKeyConstraint(['cart_id'], ['mps_cart.id'], ),
sa.ForeignKeyConstraint(['coupon_id'], ['mps_coupon.id'], ),
sa.PrimaryKeyConstraint('id')
op.create_table(
"mps_cart_coupon",
sa.Column("id", UUIDType, nullable=False),
sa.Column("cart_id", UUIDType, nullable=False),
sa.Column("coupon_id", UUIDType, nullable=False),
sa.Column("created_timestamp", sa.BigInteger(), nullable=False),
sa.ForeignKeyConstraint(
["cart_id"],
["mps_cart.id"],
),
sa.ForeignKeyConstraint(
["coupon_id"],
["mps_coupon.id"],
),
sa.PrimaryKeyConstraint("id"),
)
op.create_index(
op.f("ix_mps_cart_coupon_id"), "mps_cart_coupon", ["id"], unique=False
)
op.create_index(op.f('ix_mps_cart_coupon_id'), 'mps_cart_coupon', ['id'], unique=False)
# ### end Alembic commands ###
def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.drop_index(op.f('ix_mps_cart_coupon_id'), table_name='mps_cart_coupon')
op.drop_table('mps_cart_coupon')
op.drop_index(op.f("ix_mps_cart_coupon_id"), table_name="mps_cart_coupon")
op.drop_table("mps_cart_coupon")
# ### end Alembic commands ###

View file

@ -5,13 +5,14 @@ Revises: c263b4fba9e7
Create Date: 2022-06-09 20:52:58.235423
"""
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision = 'e6ac162d096f'
down_revision = 'c263b4fba9e7'
revision = "e6ac162d096f"
down_revision = "c263b4fba9e7"
branch_labels = None
depends_on = None
@ -19,8 +20,11 @@ from make_post_sell.models.meta import UUIDType
def upgrade():
op.add_column('mps_user', sa.Column('password_attempts', sa.Integer(), nullable=True, server_default="0"))
op.add_column(
"mps_user",
sa.Column("password_attempts", sa.Integer(), nullable=True, server_default="0"),
)
def downgrade():
op.drop_column('mps_user', 'password_attempts')
op.drop_column("mps_user", "password_attempts")

View file

@ -5,20 +5,23 @@ Revises: 5c40dfb56179
Create Date: 2019-10-29 12:14:10.391983
"""
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision = 'eadd0d69e133'
down_revision = '5c40dfb56179'
revision = "eadd0d69e133"
down_revision = "5c40dfb56179"
branch_labels = None
depends_on = None
def upgrade():
op.add_column('mps_shop', sa.Column('ribbon_text_color', sa.Unicode(length=32), nullable=True))
op.add_column(
"mps_shop", sa.Column("ribbon_text_color", sa.Unicode(length=32), nullable=True)
)
def downgrade():
op.drop_column('mps_shop', 'ribbon_text_color')
op.drop_column("mps_shop", "ribbon_text_color")

View file

@ -5,13 +5,14 @@ Revises: 96f851cbab01
Create Date: 2020-07-27 10:11:58.760190
"""
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision = 'f3aa57777025'
down_revision = '96f851cbab01'
revision = "f3aa57777025"
down_revision = "96f851cbab01"
branch_labels = None
depends_on = None
@ -20,26 +21,34 @@ from make_post_sell.models.meta import UUIDType
def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.create_table('mps_coupon',
sa.Column('id', UUIDType, nullable=False),
sa.Column('shop_id', UUIDType, nullable=False),
sa.Column('code', sa.Unicode(length=256), nullable=False),
sa.Column('description', sa.Unicode(length=256), nullable=False),
sa.Column('action_type', sa.Enum('percent-off', 'dollar-off', name='action_type'), nullable=False),
sa.Column('action_value', sa.BigInteger(), nullable=False),
sa.Column('redemptions', sa.BigInteger(), nullable=False),
sa.Column('max_redemptions', sa.BigInteger(), nullable=True),
sa.Column('max_redemptions_per_user', sa.BigInteger(), nullable=True),
sa.Column('cart_qualifier', sa.BigInteger(), nullable=True),
sa.Column('created_timestamp', sa.BigInteger(), nullable=False),
sa.Column('expired_timestamp', sa.BigInteger(), nullable=True),
sa.Column('disabled', sa.Boolean(), nullable=True),
sa.ForeignKeyConstraint(['shop_id'], ['mps_shop.id'], ),
sa.PrimaryKeyConstraint('id')
op.create_table(
"mps_coupon",
sa.Column("id", UUIDType, nullable=False),
sa.Column("shop_id", UUIDType, nullable=False),
sa.Column("code", sa.Unicode(length=256), nullable=False),
sa.Column("description", sa.Unicode(length=256), nullable=False),
sa.Column(
"action_type",
sa.Enum("percent-off", "dollar-off", name="action_type"),
nullable=False,
),
sa.Column("action_value", sa.BigInteger(), nullable=False),
sa.Column("redemptions", sa.BigInteger(), nullable=False),
sa.Column("max_redemptions", sa.BigInteger(), nullable=True),
sa.Column("max_redemptions_per_user", sa.BigInteger(), nullable=True),
sa.Column("cart_qualifier", sa.BigInteger(), nullable=True),
sa.Column("created_timestamp", sa.BigInteger(), nullable=False),
sa.Column("expired_timestamp", sa.BigInteger(), nullable=True),
sa.Column("disabled", sa.Boolean(), nullable=True),
sa.ForeignKeyConstraint(
["shop_id"],
["mps_shop.id"],
),
sa.PrimaryKeyConstraint("id"),
)
op.create_index(op.f('ix_mps_coupon_id'), 'mps_coupon', ['id'], unique=False)
op.create_index(op.f("ix_mps_coupon_id"), "mps_coupon", ["id"], unique=False)
def downgrade():
op.drop_index(op.f('ix_mps_coupon_id'), table_name='mps_coupon')
op.drop_table('mps_coupon')
op.drop_index(op.f("ix_mps_coupon_id"), table_name="mps_coupon")
op.drop_table("mps_coupon")

View file

@ -5,21 +5,28 @@ Revises: 6da97f2f913f
Create Date: 2019-08-25 13:46:12.004425
"""
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision = 'f882f2255cd1'
down_revision = '6da97f2f913f'
revision = "f882f2255cd1"
down_revision = "6da97f2f913f"
branch_labels = None
depends_on = None
def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.add_column('mps_shop', sa.Column('stripe_public_api_key', sa.Unicode(length=32), nullable=True))
op.add_column('mps_shop', sa.Column('stripe_secret_api_key', sa.Unicode(length=32), nullable=True))
op.add_column(
"mps_shop",
sa.Column("stripe_public_api_key", sa.Unicode(length=32), nullable=True),
)
op.add_column(
"mps_shop",
sa.Column("stripe_secret_api_key", sa.Unicode(length=32), nullable=True),
)
# ### end Alembic commands ###

View file

@ -5,12 +5,13 @@ Revises:
Create Date: 2019-08-05 21:46:20.974126
"""
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision = 'fd9f7e2f2b78'
revision = "fd9f7e2f2b78"
down_revision = None
branch_labels = None
depends_on = None
@ -18,11 +19,13 @@ depends_on = None
def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.add_column('mps_user', sa.Column('full_name', sa.Unicode(length=64), nullable=True))
op.add_column(
"mps_user", sa.Column("full_name", sa.Unicode(length=64), nullable=True)
)
# ### end Alembic commands ###
def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.drop_column('mps_user', 'full_name')
op.drop_column("mps_user", "full_name")
# ### end Alembic commands ###

View file

@ -18,62 +18,62 @@ from make_post_sell.models.product import get_all_products
def fix_product_s3_acls(env, dry_run=True):
"""Fix S3 ACLs for all products based on their visibility settings."""
request = env['request']
request = env["request"]
dbsession = request.dbsession
# Get S3 client and bucket name
s3_client = request.secure_uploads_client
bucket_name = request.app["bucket.secure_uploads"]
# Get all products
products = get_all_products(dbsession).all()
print(f"Found {len(products)} products to process")
print(f"Bucket: {bucket_name}")
print(f"Dry run: {dry_run}")
print("-" * 50)
processed = 0
errors = 0
for product in products:
try:
print(f"Processing product {product.id} ({product.title})")
print(f" Visibility: {product.human_visibility} ({product.visibility})")
# Check which files exist for this product
existing_files = []
for file_key in product.file_keys:
if file_key in product.extensions:
existing_files.append(file_key)
if not existing_files:
print(f" No files found, skipping")
continue
print(f" Files: {existing_files}")
# Show what ACLs would be applied
for file_key in existing_files:
acl = product.get_s3_acl_for_file_key(file_key)
print(f" {file_key}: {acl}")
if not dry_run:
# Actually update the ACLs
product.update_s3_acls(s3_client, bucket_name)
print(f" ✓ Updated ACLs")
else:
print(f" (Dry run - no changes made)")
processed += 1
except Exception as e:
print(f" ✗ Error processing product {product.id}: {e}")
errors += 1
print()
print("-" * 50)
print(f"Summary:")
print(f" Processed: {processed}")
@ -83,13 +83,18 @@ def fix_product_s3_acls(env, dry_run=True):
def main():
"""Main entry point."""
parser = argparse.ArgumentParser(description='Fix S3 ACLs for products based on visibility')
parser.add_argument('config_uri', help='Configuration file (e.g., development.ini)')
parser.add_argument('--execute', action='store_true',
help='Actually make changes (default is dry run)')
parser = argparse.ArgumentParser(
description="Fix S3 ACLs for products based on visibility"
)
parser.add_argument("config_uri", help="Configuration file (e.g., development.ini)")
parser.add_argument(
"--execute",
action="store_true",
help="Actually make changes (default is dry run)",
)
args = parser.parse_args()
# Bootstrap the Pyramid application
with bootstrap(args.config_uri) as env:
try:
@ -102,5 +107,5 @@ def main():
sys.exit(1)
if __name__ == '__main__':
main()
if __name__ == "__main__":
main()

View file

@ -20,7 +20,7 @@ def setup_models(dbsession, settings):
settings["app.root_domain"],
"000-000-0000",
"none",
"Reserved shop for SaaS root domain."
"Reserved shop for SaaS root domain.",
)
root_domain_shop.domain_name = settings["app.root_domain"]
@ -28,8 +28,7 @@ def setup_models(dbsession, settings):
dbsession.flush()
root_domain_owner = get_or_create_user_by_email(
dbsession,
settings["app.root_domain_owner_email"]
dbsession, settings["app.root_domain_owner_email"]
)
dbsession.add(root_domain_owner)
@ -44,8 +43,8 @@ def setup_models(dbsession, settings):
def parse_args(argv):
parser = argparse.ArgumentParser()
parser.add_argument(
'config_uri',
help='Configuration file, e.g., development.ini',
"config_uri",
help="Configuration file, e.g., development.ini",
)
return parser.parse_args(argv[1:])
@ -60,11 +59,12 @@ def main(argv=sys.argv):
models.meta.Base.metadata.create_all(engine)
try:
with env['request'].tm:
dbsession = env['request'].dbsession
#setup_models(dbsession, settings)
with env["request"].tm:
dbsession = env["request"].dbsession
# setup_models(dbsession, settings)
except OperationalError:
print('''
print(
"""
Pyramid is having a problem using your SQL database. The problem
might be caused by one of the following things:
@ -74,4 +74,5 @@ might be caused by one of the following things:
2. Your database server may not be running. Check that the
database server referred to by the "sqlalchemy.url" setting in
your "development.ini" file is running.
''')
"""
)

View file

@ -2,12 +2,12 @@ from .. import models
def setup(env):
request = env['request']
request = env["request"]
# start a transaction
request.tm.begin()
# inject some vars into the shell builtins
env['tm'] = request.tm
env['dbsession'] = request.dbsession
env['models'] = models
env["tm"] = request.tm
env["dbsession"] = request.dbsession
env["models"] = models

File diff suppressed because it is too large Load diff

View file

@ -14,6 +14,7 @@ def user_required(
max_redirects=3, # Limit the number of redirects
):
"""This view requires that the request has a user."""
def wrapped(fn):
def inner(request):
if request.user and request.user.authenticated:
@ -24,7 +25,9 @@ def user_required(
redirect_count = request.session.get("redirect_count", 0)
if redirect_count >= max_redirects:
# Redirect to a safe default page if max redirects reached
request.session.flash(("Too many redirects, please try again later.", "error"))
request.session.flash(
("Too many redirects, please try again later.", "error")
)
return HTTPFound(request.route_url("home"))
# Increment redirect count
request.session["redirect_count"] = redirect_count + 1
@ -34,9 +37,12 @@ def user_required(
if redirect_to_route_name:
return HTTPFound(request.route_url(redirect_to_route_name))
return HTTPFound(get_referer_or_home(request))
return inner
return wrapped
# view decorator.
def shop_is_ready_required(
flash_msg="Sorry, this shop is not ready to make sales yet. Please try again later.",

View file

@ -472,6 +472,7 @@ def cart_checkout(request):
xmr_processor_enabled = False
if request.monero_enabled:
from ..models.crypto_processor import CryptoProcessor
xmr_processor = (
request.dbsession.query(CryptoProcessor)
.filter(

View file

@ -26,44 +26,52 @@ def comment_new(request):
product_id = request.params.get("product_id")
parent_id = request.params.get("parent_id") # For replies
data = request.params.get("data", "").strip()
if not product_id or not data:
request.session.flash(("Comment content is required", "error"))
return HTTPFound(location=get_referer_or_home(request))
# Get product and validate
product = get_product_by_id(request.dbsession, product_id)
if not product:
return HTTPNotFound("Product not found")
shop = product.shop
# Check if user can comment
can_comment, error_msg = True, None
if not shop.comments_enabled:
can_comment, error_msg = False, "Comments are disabled for this shop"
elif shop.comments_require_purchase:
# Check if user has purchased this product
from ..models.user_product import UserProduct
user_product = request.dbsession.query(UserProduct).filter(
UserProduct.user_id == request.user.id,
UserProduct.product_id == product_id
).first()
user_product = (
request.dbsession.query(UserProduct)
.filter(
UserProduct.user_id == request.user.id,
UserProduct.product_id == product_id,
)
.first()
)
if not user_product:
can_comment, error_msg = False, "You must purchase this product to leave a comment"
can_comment, error_msg = (
False,
"You must purchase this product to leave a comment",
)
if not can_comment:
request.session.flash((error_msg, "error"))
return HTTPFound(location=get_referer_or_home(request))
# Create comment
comment = Comment()
comment.product_id = product_id
comment.user_id = request.user.id
comment.set_data(data)
# Set approval status based on shop settings
if shop.comments_require_approval:
# Shop owners and editors get auto-approved
@ -74,7 +82,7 @@ def comment_new(request):
else:
# No approval required
comment.approved = True
# Handle reply (set parent and root)
if parent_id:
parent_comment = get_comment_by_id(request.dbsession, parent_id)
@ -86,15 +94,15 @@ def comment_new(request):
# Root comment
comment.root_id = comment.id
comment.graph_depth = 0
request.dbsession.add(comment)
request.dbsession.flush()
if comment.approved:
request.session.flash(("Comment posted successfully", "success"))
else:
request.session.flash(("Comment submitted for approval", "success"))
# Redirect to the product page with fragment to scroll to the new comment
product_url = product.absolute_url(request)
return HTTPFound(location=f"{product_url}#comment-{comment.id}")
@ -106,18 +114,18 @@ def comment_reply_get(request):
"""Show reply to comment form."""
comment_id = request.matchdict["comment_id"]
parent_comment = get_comment_by_id(request.dbsession, comment_id)
if not parent_comment:
return HTTPNotFound("Comment not found")
# Check if user can reply
shop = parent_comment.product.shop
can_comment, error_msg = parent_comment.can_user_comment(request.user, shop)
if not can_comment:
request.session.flash((error_msg, "error"))
return HTTPFound(location=parent_comment.product.absolute_url(request))
return {
"parent_comment": parent_comment,
"product": parent_comment.product,
@ -131,24 +139,26 @@ def comment_reply_post(request):
"""Create a reply to a comment."""
comment_id = request.matchdict["comment_id"]
parent_comment = get_comment_by_id(request.dbsession, comment_id)
if not parent_comment:
return HTTPNotFound("Comment not found")
# Check if user can reply
shop = parent_comment.product.shop
can_comment, error_msg = parent_comment.can_user_comment(request.user, shop)
if not can_comment:
request.session.flash((error_msg, "error"))
return HTTPFound(location=parent_comment.product.absolute_url(request))
data = request.params.get("data", "").strip()
if not data:
request.session.flash(("Comment content is required", "error"))
return HTTPFound(location=request.route_url("comment_reply", comment_id=comment_id))
return HTTPFound(
location=request.route_url("comment_reply", comment_id=comment_id)
)
# Create reply comment
comment = Comment()
comment.product_id = parent_comment.product_id
@ -157,7 +167,7 @@ def comment_reply_post(request):
comment.root_id = parent_comment.root_id or parent_comment.id
comment.set_data(data)
comment.recompute_depth()
# Set approval status based on shop settings
if shop.comments_require_approval:
# Shop owners and editors get auto-approved
@ -168,15 +178,15 @@ def comment_reply_post(request):
else:
# No approval required
comment.approved = True
request.dbsession.add(comment)
request.dbsession.flush()
if comment.approved:
request.session.flash(("Reply posted successfully", "success"))
else:
request.session.flash(("Reply submitted for approval", "success"))
# Redirect to the product page with fragment to scroll to the new comment
product_url = parent_comment.product.absolute_url(request)
return HTTPFound(location=f"{product_url}#comment-{comment.id}")
@ -188,15 +198,17 @@ def comment_edit_get(request):
"""Show edit comment form."""
comment_id = request.matchdict["comment_id"]
comment = get_comment_by_id(request.dbsession, comment_id)
if not comment:
return HTTPNotFound("Comment not found")
# Check permissions
shop = comment.product.shop
if comment.user_id != request.user.id and not comment.can_user_moderate(request.user, shop):
if comment.user_id != request.user.id and not comment.can_user_moderate(
request.user, shop
):
return HTTPForbidden("You can only edit your own comments")
return {
"comment": comment,
"product": comment.product,
@ -210,24 +222,28 @@ def comment_edit_post(request):
"""Update a comment."""
comment_id = request.matchdict["comment_id"]
comment = get_comment_by_id(request.dbsession, comment_id)
if not comment:
return HTTPNotFound("Comment not found")
# Check permissions
shop = comment.product.shop
if comment.user_id != request.user.id and not comment.can_user_moderate(request.user, shop):
if comment.user_id != request.user.id and not comment.can_user_moderate(
request.user, shop
):
return HTTPForbidden("You can only edit your own comments")
data = request.params.get("data", "").strip()
if not data:
request.session.flash(("Comment content is required", "error"))
return HTTPFound(location=request.route_url("comment_edit", comment_id=comment_id))
return HTTPFound(
location=request.route_url("comment_edit", comment_id=comment_id)
)
comment.set_data(data)
request.session.flash(("Comment updated successfully", "success"))
# Redirect back to product page with fragment to scroll to the edited comment
product_url = comment.product.absolute_url(request)
return HTTPFound(location=f"{product_url}#comment-{comment.id}")
@ -239,20 +255,22 @@ def comment_delete(request):
"""Delete a comment."""
comment_id = request.matchdict["comment_id"]
comment = get_comment_by_id(request.dbsession, comment_id)
if not comment:
return HTTPNotFound("Comment not found")
# Check permissions
shop = comment.product.shop
if comment.user_id != request.user.id and not comment.can_user_moderate(request.user, shop):
if comment.user_id != request.user.id and not comment.can_user_moderate(
request.user, shop
):
return HTTPForbidden("You can only delete your own comments")
product_url = comment.product.absolute_url(request)
# Soft delete using disable method
comment.disable()
request.session.flash(("Comment deleted successfully", "success"))
return HTTPFound(location=product_url)
@ -263,18 +281,18 @@ def comment_approve(request):
"""Approve a comment (shop owners and editors only)."""
comment_id = request.matchdict["comment_id"]
comment = get_comment_by_id(request.dbsession, comment_id)
if not comment:
return HTTPNotFound("Comment not found")
# Check permissions - shop owners and editors can approve
shop = comment.product.shop
if not comment.can_user_moderate(request.user, shop):
return HTTPForbidden("Only shop owners and editors can approve comments")
comment.approved = True
comment.stamp_updated_timestamp()
request.session.flash(("Comment approved", "success"))
return HTTPFound(location=get_referer_or_home(request))
@ -285,18 +303,18 @@ def comment_unapprove(request):
"""Unapprove a comment (shop owners and editors only)."""
comment_id = request.matchdict["comment_id"]
comment = get_comment_by_id(request.dbsession, comment_id)
if not comment:
return HTTPNotFound("Comment not found")
# Check permissions - shop owners and editors can unapprove
shop = comment.product.shop
if not comment.can_user_moderate(request.user, shop):
return HTTPForbidden("Only shop owners and editors can moderate comments")
comment.approved = False
comment.stamp_updated_timestamp()
request.session.flash(("Comment unapproved", "success"))
return HTTPFound(location=get_referer_or_home(request))
@ -307,17 +325,21 @@ def comment_undelete(request):
"""Undelete a comment (shop owners and editors only)."""
comment_id = request.matchdict["comment_id"]
comment = get_comment_by_id(request.dbsession, comment_id)
if not comment:
return HTTPNotFound("Comment not found")
# Check permissions - comment owner, shop owners, or shop editors can undelete
shop = comment.product.shop
if comment.user_id != request.user.uuid_str and not (shop.is_owner(request.user) or shop.is_editor(request.user)):
return HTTPForbidden("Only comment owners, shop owners, and shop editors can undelete comments")
if comment.user_id != request.user.uuid_str and not (
shop.is_owner(request.user) or shop.is_editor(request.user)
):
return HTTPForbidden(
"Only comment owners, shop owners, and shop editors can undelete comments"
)
# Re-enable the comment
comment.enable()
request.session.flash(("Comment restored successfully", "success"))
return HTTPFound(location=comment.product.absolute_url(request))
return HTTPFound(location=comment.product.absolute_url(request))

View file

@ -67,11 +67,9 @@ def content(request):
# Load comments for the content
from ..models.comment import get_comments_for_product
comments = get_comments_for_product(
request.dbsession,
product.id,
shop=product.shop,
user=request.user
request.dbsession, product.id, shop=product.shop, user=request.user
)
return {

View file

@ -71,13 +71,23 @@ def crypto_processor_settings(request):
client = get_client_from_settings(request.registry.settings)
# Create new account with shop name as label
account_label = f"shop_{shop.name[:20]}_{shop.id[:8]}"
# Create new account with shop UUID as label
account_label = f"mps-shop-{shop.uuid_str}"
result = client._call("create_account", {"label": account_label})
account_index = result.get("account_index")
if account_index is not None:
processor.wallet_label = str(account_index)
# Tag the account with the shop name for easier identification
try:
client._call(
"tag_accounts",
{"tag": shop.name, "accounts": [account_index]},
)
except Exception as tag_error:
# Don't fail account creation if tagging fails
pass
else:
raise Exception("Failed to get account_index from create_account")

View file

@ -51,8 +51,12 @@ def ask_for_on_demand_tls(request):
"""
domain_name_requesting_tls = request.params.get("domain")
if domain_name_requesting_tls:
query = text("SELECT EXISTS (SELECT 1 FROM mps_shop WHERE domain_name = :domain_name)")
result = request.dbsession.execute(query, {'domain_name': domain_name_requesting_tls})
query = text(
"SELECT EXISTS (SELECT 1 FROM mps_shop WHERE domain_name = :domain_name)"
)
result = request.dbsession.execute(
query, {"domain_name": domain_name_requesting_tls}
)
exists = result.scalar()
if exists:
return Response(status=200)

View file

@ -84,13 +84,11 @@ def product(request):
if product.has_product_file:
product_size = product.human_product_file_bytes
# Load comments for the product
# Load comments for the product
from ..models.comment import get_comments_for_product
comments = get_comments_for_product(
request.dbsession,
product.id,
shop=product.shop,
user=request.user
request.dbsession, product.id, shop=product.shop, user=request.user
)
return {
@ -248,7 +246,11 @@ def product_edit(request):
if visibility != product.visibility:
product_modified = True
product.set_visibility(visibility, request.secure_uploads_client, request.app["bucket.secure_uploads"])
product.set_visibility(
visibility,
request.secure_uploads_client,
request.app["bucket.secure_uploads"],
)
request.session.flash(("You updated the product's visibility.", "success"))
if price != product.price:

View file

@ -150,19 +150,19 @@ def shop_location_form(request):
"hours_open": shop_location.hours_open if shop_location else "",
"local_pickup": shop_location.local_pickup if shop_location else False,
"local_delivery": shop_location.local_delivery if shop_location else False,
"local_delivery_rate": shop_location.local_delivery_rate
if shop_location
else "0.00",
"local_delivery_rate": (
shop_location.local_delivery_rate if shop_location else "0.00"
),
"local_shipping": shop_location.local_shipping if shop_location else False,
"local_shipping_rate": shop_location.local_shipping_rate
if shop_location
else "0.00",
"international_shipping": shop_location.international_shipping
if shop_location
else False,
"international_shipping_rate": shop_location.international_shipping_rate
if shop_location
else "0.00",
"local_shipping_rate": (
shop_location.local_shipping_rate if shop_location else "0.00"
),
"international_shipping": (
shop_location.international_shipping if shop_location else False
),
"international_shipping_rate": (
shop_location.international_shipping_rate if shop_location else "0.00"
),
}

View file

@ -11,7 +11,7 @@ from . import user_required
def get_enabled_coins(request):
"""Get list of enabled cryptocurrency types from all shops."""
from ..models.crypto_processor import CryptoProcessor
# Get distinct coin types from all enabled crypto processors
enabled_coins = (
request.dbsession.query(CryptoProcessor.coin_type)
@ -19,7 +19,7 @@ def get_enabled_coins(request):
.distinct()
.all()
)
# Return list of coin type strings
return [coin_type for (coin_type,) in enabled_coins]

View file

@ -1,6 +1,7 @@
import os
from setuptools import setup, find_packages
def parse_requirements(filename):
"""
Read and parse a requirements file, ignoring comments and blank lines.
@ -10,6 +11,7 @@ def parse_requirements(filename):
lines = f.read().splitlines()
return [line.strip() for line in lines if line.strip() and not line.startswith("#")]
# Determine the runtime requirements.
install_requires = parse_requirements("requirements.py3.txt")