Skip to content

Commit

Permalink
Kingfisher 21 (#906)
Browse files Browse the repository at this point in the history
* Update devapp1.yml

New Release

* mansory grid (#915)

* allow super admin to log in and remove unreachable codes (#911)

* email notification on requesting permission and approve/reject (#909)

* Issue 871 Guest should not have access to Studio (#908)

* hide studio from guest menu

* server side blocking

* update script (#907)

* Display status and visibility on stages list (#922)

* display status and visibility to dashboard

* mutation update status, visibility

* Issue 807 Keep backdrop animation while changing opacity (#917)

* fix creating media in studio

* keep animation while changing opacity

* Issue 912 Scaffolding script enhancement (#916)

* demo stage visibility and cover image

* media size detection

* all user should be able to use demo stage

* downsize

* Issue 918 Deleting player (#924)

* fix performance foreign key and typo

* cascade totp

* How did this old domain name get back in here???

* Add filter images and fix error create image curtain type (#928)

* create image type curtain and add filter image

* update style

* format code

* change style image grid (#929)

* change style image grid

* Display center image

* troubleshooting (#930)

* Issue 925 Clear archived events on stage delete (#933)

* troubleshooting

* clear archived events of abandoned stage

* options to send as BCC

* Revert "Issue 925 Clear archived events on stage delete (#933)"

This reverts commit c29c211.

* Issue 925 Clear archived events on stage delete (#933)

* troubleshooting

* clear archived events of abandoned stage

* Revert "options to send as BCC"

This reverts commit 00ae6a6.

* options to send as BCC (#936)

* Issue 931 Making our software i18n - Dashboard (#935)

* integrating vue-in18n

* convert hard coded string to i18n component

* npm script and language selector

* vietnames translation

* persist locale to local storage

Co-authored-by: gloriajw <gloriajw@users.noreply.github.com>

* fix bcc recipients not receing email

* fix bcc not updating

* Issue 819 No required to field (#937)

* allow email to be sent with bcc only

* translate text

* Issue 931 I18n - Studio (#939)

* setup i18n in studio

* convert string in html to translatable component

* translatable column and dragzone

* Issue 932 stripe intergration (#938)

* add payment schema

* Integrate donate to upstage

* validate input card

* Fix format exp

* Fix format custom amount

* Validate input

* remove key

* optional hover effect in prop link (#941)

Co-authored-by: Hồng Phát <hongphat.js@gmail.com>
Co-authored-by: TuyetGiang <30744004+TuyetGiang@users.noreply.github.com>
Co-authored-by: gloriajw <aagg@comcast.net>
  • Loading branch information
4 people committed May 28, 2022
1 parent 1b63ca5 commit a4bfb8c
Show file tree
Hide file tree
Showing 115 changed files with 2,155 additions and 611 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/devapp1.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ name: DEVAPP1 CI
on:
# Triggers the workflow on push or approved pull request on R1-2021 branch
push:
branches: [ Schildbrau-20 ]
branches: [ Kingfisher-21 ]

# Allows you to run this workflow manually from the Actions tab
workflow_dispatch:
Expand All @@ -32,7 +32,7 @@ jobs:
script: |
cd /home/upstage/upstage/ui/dashboard/
git fetch
git checkout Schildbrau-20
git checkout Kingfisher-21
git pull
yarn
yarn build:dev
Expand Down
47 changes: 47 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -248,5 +248,52 @@ Finally, pass the incoming request to `UpStage Service`
        }
}
```

## Troubleshooting

### Stages offline when entering with firefox

If you are using firefox and doesn't have the red `LIVE` status on the top right when entering a stage although the stage is live using other browsers, it's because the version of Mosquitto (MQTT Broker) you are using doesn't use `libwebsockets` or it's using a version of `libwebsockets` that having an issue in protocol handling of HTTP2. Whilst Chromium does not attempt a HTTP2 connection in this case, Firefox tries it first and gets a denied reply from the server. More explanation can be found [here](https://www.bluhm-de.com/content/os-tools/en/applications/mqtt/websocket-connections-fail-with-javascript-paho-client.html).

The solution is to build `libwebsockets` and `mosquitto` from source and use it instead of the one provided by your distro.

Instruction on how to do this:

```bash
apt install libssl-dev xsltproc docbook-xsl
git clone https://github.com/warmcat/libwebsockets.git
cd libwebsockets
mkdir build
cd build
cmake .. -DLWS_WITH_HTTP2=OFF
make
make install
ldconfig
cd ../..
git clone https://github.com/eclipse/mosquitto.git
cd mosquitto
make install WITH_WEBSOCKETS=yes WITH_CJSON=no
sudo systemctl edit mosquitto.service
```

Put this into the override file of the service:

```ini
[Unit]
ConditionPathExists=/etc/mosquitto/mosquitto.conf
Requires=network.target

[Service]
Type=
Type=simple
ExecStart=
ExecStart=/usr/local/sbin/mosquitto -c /etc/mosquitto/mosquitto.conf
```
Finally restart the service

```bash
sudo systemctl restart mosquitto.service
```

## License
[GPL-3.0 License](https://github.com/upstage-org/upstage/blob/main/LICENSE)
35 changes: 5 additions & 30 deletions auth/auth_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -254,8 +254,11 @@ def call_login_logout(app_entry=True,num_value=None):
).filter(User.active==True).first()

if user:
if not decrypt(user.password) == password:
return make_response(jsonify({"error": "Bad email or password (16)"}), 403)
try:
if not decrypt(user.password) == password:
return make_response(jsonify({"error": "Incorrect username or password (16)"}), 403)
except:
return make_response(jsonify({"error": "Signature did not match digest. Please contact admin to make sure that cipher key is correctly set up (18)"}), 403)
else:
if '@' in username:
user = DBSession.query(User).filter(User.email==username
Expand All @@ -274,34 +277,6 @@ def call_login_logout(app_entry=True,num_value=None):
else:
return make_response(jsonify({"error": "Your account has been successfully created but not approved yet.<br/>Please wait for approval or contact UpStage Admin for support!", "level": "warning"}), 403)

# Re-send their signup code.
existing_code = get_security_code(user.id,SIGNUP_VALIDATION)
if not existing_code:
existing_code = new_security_code(user.id,SIGNUP_VALIDATION_MISSING_1ST_CODE)
'''
raise LostCodeError(
"Something weird happened in the system, and this person's code got lost: user id {}".format(user.id)
)
'''

if not send_acct_verification_code(user.phone,existing_code): # always sms
app.logger.warning(f"Tried to send this user a verification code but their phone number is invalid: user_id {user.id} phone {user.phone} ")

email_verification_code(user.email,existing_code)
return make_response(jsonify({"user_id":user.id,
"message": "User needs to verify account"}), 409)

# Admin Logins from the portal will pass a TOTP value that must be verified.
# Service providers will have an SMS code they have entered.

# Kludge: uncomment the below line. remove the line after.
if user.role in (SUPER_ADMIN,):
if not verify_user_totp(user,num_value):
return make_response(jsonify({"error": "Invalid code."}), 403)

elif user.role in (PLAYER,GUEST,ADMIN) and not app_entry:
pass # password checked-out, that's all we need.

# We may also have a fb/g login, if this is the first time a user is logging
# in via fb/g.
# If it's not the first time, we only have the fb/g login, and it's already mapped
Expand Down
6 changes: 4 additions & 2 deletions config/schema.py
Original file line number Diff line number Diff line change
Expand Up @@ -151,15 +151,17 @@ class Arguments:
required=True, description="The body of the email. HTML is allowed.")
recipients = graphene.String(
required=True, description="The recipients of the email. Comma separated.")
bcc = graphene.String(
required=False, description="The bcc recipients of the email. Comma separated.")

@jwt_required()
def mutate(self, info, subject, body, recipients):
def mutate(self, info, subject, body, recipients, bcc):
code, error, user, timezone = current_user()
if not user.role in (ADMIN, SUPER_ADMIN):
raise Exception(
"Only Admin can send notification emails!")

send(recipients, subject, body)
send(recipients, subject, body, bcc)
return SendEmail(success=True)


Expand Down
2 changes: 2 additions & 0 deletions config/settings/your_hostname.py
Original file line number Diff line number Diff line change
Expand Up @@ -118,3 +118,5 @@
CIPHER_KEY='' # Paste the result from fernet_crypto.py
SECRET_KEY='' # Paste the result from running __init__.py
STREAM_KEY='' # Paste the secret key from node media server config
STRIPE_KEY = ''
STRIPE_PRODUCT_ID = ''
23 changes: 15 additions & 8 deletions mail/mail_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,30 +6,37 @@
import os
import re
from config.models import Config
from config.project_globals import DBSession
from config.project_globals import ScopedSession

from config.settings import (EMAIL_HOST, EMAIL_PORT,
EMAIL_HOST_USER, EMAIL_HOST_PASSWORD, EMAIL_HOST_DISPLAY_NAME,
ADMIN_EMAIL, ENV_TYPE)

def send_sync(to, subject, content):
subject_prefix = DBSession.query(Config).filter(Config.name == 'EMAIL_SUBJECT_PREFIX').first().value
def send_sync(to, subject, content, bcc=None):
with ScopedSession() as local_db_session:
subject_prefix = local_db_session.query(Config).filter(Config.name == 'EMAIL_SUBJECT_PREFIX').first().value
msg = MIMEText(content, "html")
msg["Subject"] = f'{subject_prefix}: {subject}'
if subject_prefix:
msg["Subject"] = f'{subject_prefix}: {subject}'
else:
msg["Subject"] = subject
# some SMTP servers will do this automatically, not all
msg["From"] = f'{EMAIL_HOST_DISPLAY_NAME} <{EMAIL_HOST_USER}>'
msg["To"] = to
msg["Bcc"] = ADMIN_EMAIL
if bcc:
msg["Bcc"] = ADMIN_EMAIL + ',' + bcc
else:
msg["Bcc"] = ADMIN_EMAIL

# Always use TLS.
conn = SMTP(EMAIL_HOST, EMAIL_PORT)
conn.set_debuglevel(False)
conn.login(EMAIL_HOST_USER, EMAIL_HOST_PASSWORD)
try:
recipients = re.split(r'[,;]\s*', '{},{}'.format(to, ADMIN_EMAIL))
recipients = re.split(r'[,;]\s*', '{},{}'.format(msg["To"], msg["Bcc"]))
conn.sendmail(EMAIL_HOST_USER, recipients, msg.as_string())
finally:
conn.quit()

def send(to, subject, content):
Process(target=send_sync, args=(to, subject, content)).start()
def send(to, subject, content, bcc=None):
Process(target=send_sync, args=(to, subject, content, bcc)).start()
27 changes: 27 additions & 0 deletions mail/templates.py
Original file line number Diff line number Diff line change
Expand Up @@ -82,4 +82,31 @@ def admin_registration_notification(user, approval_url):
<br>
<b>Email:</b> {user.email}
{footer}
"""

def request_permission_for_media(user, media, note, studio_url):
return f"""
<p>
Hi <b>{display_user(media.owner)}</b>,
<br>
<br>
{display_user(user)} has requested permission to use your media <b>{media.name}</b>. Please go to the <a href="{studio_url}">Studio</a> and click on the Notification icon to approve or deny the request.
<br>
Purpose: {note}
<br>
<br>
{footer}
"""

def permission_response_for_media(user, media, note, approved, studio_url):
return f"""
<p>
Hi <b>{display_user(user)}</b>,
<br>
<br>
Your permission request for <b>{media.name}</b> with purpose \"{note}\" has been {'approved' if approved else 'denied'} by the owner.
{f'<br><br>You can now use the media in the <a href="{studio_url}">Studio</a>.' if approved else ''}
<br>
<br>
{footer}
"""
117 changes: 117 additions & 0 deletions payment/payment.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
# -*- coding: iso8859-15 -*-
import os
import sys

appdir = os.path.abspath(os.path.dirname(__file__))
projdir = os.path.abspath(os.path.join(appdir, '..'))
if projdir not in sys.path:
sys.path.append(appdir)
sys.path.append(projdir)

import stripe
from config.settings import STRIPE_KEY, STRIPE_PRODUCT_ID


stripe.api_key = STRIPE_KEY

ACCEPT_TYPE = ['card']
ACCEPT_CURRENCIES = ['usd']


def generate_card_token(card_number, exp_month, exp_year, cvc):
''' Generate card token '''
data = stripe.Token.create(
card={
"number": str(card_number),
"exp_month": int(exp_month),
"exp_year": int(exp_year),
"cvc": str(cvc),
})
card_token = data['id']

return card_token


def create_payment_charge(token_id, amount):
''' Create payment charge '''
payment = stripe.Charge.create(
amount=int(amount)*100, # convert amount to cents
currency='usd',
description='Example charge',
source=token_id,
)

payment_check = payment['paid'] # return True for payment

return payment_check


def create_subscription(payment_method_id, customer_id, price_id):
''' Create a subcription '''
stripe.PaymentMethod.attach(payment_method_id, customer=customer_id)
# Set the default payment method on the customer
stripe.Customer.modify(customer_id,
invoice_settings={
'default_payment_method': payment_method_id,
},
)
# Create the subscription
# Subscribe the user to the subscription created
subscription = stripe.Subscription.create(
customer=customer_id,
items=[{"price": price_id, }],
expand=["latest_invoice.payment_intent"]
)
return subscription


def get_subscription(subscription_id):
''' Get subcription by id'''
subscription = stripe.Subscription.retrieve(subscription_id)
return subscription


def cancel_subscription(subscription_id):
''' Cancel a subcription '''
deletedSubscription = stripe.Subscription.delete(subscription_id)
return deletedSubscription


def create_customer(email):
''' Create new customer '''
customer = stripe.Customer.create(email=email)
return customer


def update_email_customer(customer_id, email):
''' Update customer email '''
customer = stripe.Customer.modify(customer_id, email=email)
return customer


def create_payment_card(card_number, exp_month, exp_year, cvc):
''' Create payment by card '''
payment_method = stripe.PaymentMethod.create(
type="card",
card={
"number": str(card_number),
"exp_month": int(exp_month),
"exp_year": int(exp_year),
"cvc": str(cvc),
})
return payment_method


def create_price(unit_amount, currency):
''' Create new price '''
price = stripe.Price.create(
unit_amount=int(unit_amount)*100,
currency=str(currency),
recurring={"interval": "year"},
product=STRIPE_PRODUCT_ID
)
return price


if __name__ == "__main__":
print(stripe.Product.create(name="Upstage").id)
Loading

0 comments on commit a4bfb8c

Please sign in to comment.