Compare commits
No commits in common. "f4918d4815687dd217872762b97eb1b8b1e73a0e" and "c0e1c2eb7e9ac8f2c5dd2aef07ffa87e24802976" have entirely different histories.
f4918d4815
...
c0e1c2eb7e
7 changed files with 31 additions and 87 deletions
|
|
@ -5,12 +5,12 @@
|
||||||
- Switched to Quart framework. This implies everything is `async def` now.
|
- Switched to Quart framework. This implies everything is `async def` now.
|
||||||
- **BREAKING**: `SERVER_NAME` env variable now contains the domain name. `DOMAIN_NAME` has been removed.
|
- **BREAKING**: `SERVER_NAME` env variable now contains the domain name. `DOMAIN_NAME` has been removed.
|
||||||
- libsuou bumped to 0.6.0
|
- libsuou bumped to 0.6.0
|
||||||
- Added several REST routes. Change needed due to pending [frontend separation](https://nekode.yusur.moe/yusur/vigil).
|
- Added several REST routes. Change needed due to pending frontend separation.
|
||||||
- Deprecated the old web routes except for `/report` and `/admin`
|
- Deprecated the old web routes except for `/report` and `/admin`
|
||||||
|
|
||||||
## 0.4.0
|
## 0.4.0
|
||||||
|
|
||||||
- Added dependency to [SUOU](https://github.com/yusurko/suou) library
|
- Added dependency to [SUOU](https://github.com/sakuragasaki46/suou) library
|
||||||
- Users can now block each other
|
- Users can now block each other
|
||||||
+ Blocking a user prevents them from seeing your comments, posts (standalone or in feed) and profile
|
+ Blocking a user prevents them from seeing your comments, posts (standalone or in feed) and profile
|
||||||
- Added user strikes: a strike logs the content of a removed message for future use
|
- Added user strikes: a strike logs the content of a removed message for future use
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,7 @@ from suou import twocolon_list, WantsContentType
|
||||||
|
|
||||||
from .colors import color_themes, theme_classes
|
from .colors import color_themes, theme_classes
|
||||||
|
|
||||||
__version__ = '0.5.0-dev47'
|
__version__ = '0.5.0-dev45'
|
||||||
|
|
||||||
APP_BASE_DIR = os.path.dirname(os.path.dirname(__file__))
|
APP_BASE_DIR = os.path.dirname(os.path.dirname(__file__))
|
||||||
|
|
||||||
|
|
@ -40,12 +40,11 @@ class AppConfig(ConfigOptions):
|
||||||
server_name = ConfigValue()
|
server_name = ConfigValue()
|
||||||
force_server_name = ConfigValue(cast=yesno, default=True)
|
force_server_name = ConfigValue(cast=yesno, default=True)
|
||||||
private_assets = ConfigValue(cast=ssv_list)
|
private_assets = ConfigValue(cast=ssv_list)
|
||||||
|
# deprecated
|
||||||
|
jquery_url = ConfigValue(default='https://ajax.googleapis.com/ajax/libs/jquery/3.7.1/jquery.min.js')
|
||||||
app_is_behind_proxy = ConfigValue(cast=int, default=0)
|
app_is_behind_proxy = ConfigValue(cast=int, default=0)
|
||||||
impressum = ConfigValue(cast=twocolon_list, default='')
|
impressum = ConfigValue(cast=twocolon_list, default='')
|
||||||
create_guild_threshold = ConfigValue(cast=int, default=15, prefix='freak_')
|
create_guild_threshold = ConfigValue(cast=int, default=15, prefix='freak_')
|
||||||
# v-- deprecated --v
|
|
||||||
jquery_url = ConfigValue(default='https://ajax.googleapis.com/ajax/libs/jquery/3.7.1/jquery.min.js')
|
|
||||||
# ^----------------^
|
|
||||||
|
|
||||||
app_config = AppConfig()
|
app_config = AppConfig()
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,16 +1,12 @@
|
||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
import datetime
|
|
||||||
import sys
|
|
||||||
from typing import Iterable, TypeVar
|
from typing import Iterable, TypeVar
|
||||||
import logging
|
|
||||||
|
|
||||||
from quart import render_template, session
|
from quart import render_template, session
|
||||||
from quart import abort, Blueprint, redirect, request, url_for
|
from quart import abort, Blueprint, redirect, request, url_for
|
||||||
from pydantic import BaseModel, Field
|
from pydantic import BaseModel
|
||||||
from quart_auth import current_user, login_required, login_user, logout_user
|
from quart_auth import current_user, login_required, login_user, logout_user
|
||||||
from quart_schema import validate_request, validate_response
|
from quart_schema import validate_request, validate_response
|
||||||
from quart_wtf.csrf import generate_csrf
|
|
||||||
from sqlalchemy import delete, insert, select
|
from sqlalchemy import delete, insert, select
|
||||||
from suou import Snowflake, deprecated, makelist, not_implemented, want_isodate
|
from suou import Snowflake, deprecated, makelist, not_implemented, want_isodate
|
||||||
|
|
||||||
|
|
@ -25,7 +21,6 @@ from freak.search import SearchQuery
|
||||||
from ..models import Comment, Guild, Post, PostUpvote, User, db
|
from ..models import Comment, Guild, Post, PostUpvote, User, db
|
||||||
from .. import UserLoader, app, app_config, __version__ as freak_version, csrf
|
from .. import UserLoader, app, app_config, __version__ as freak_version, csrf
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
|
||||||
_T = TypeVar('_T')
|
_T = TypeVar('_T')
|
||||||
|
|
||||||
bp = Blueprint('rest', __name__, url_prefix='/v1')
|
bp = Blueprint('rest', __name__, url_prefix='/v1')
|
||||||
|
|
@ -69,13 +64,8 @@ async def oath():
|
||||||
## pull csrf token from session
|
## pull csrf token from session
|
||||||
csrf_tok = session['csrf_token']
|
csrf_tok = session['csrf_token']
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
try:
|
print(e)
|
||||||
logger.warning('CSRF token regenerated!')
|
abort(503, "csrf_token is null")
|
||||||
csrf_tok = session['csrf_token'] = generate_csrf()
|
|
||||||
except Exception as e2:
|
|
||||||
print(e, e2)
|
|
||||||
abort(503, "csrf_token is null")
|
|
||||||
|
|
||||||
return dict(
|
return dict(
|
||||||
## XXX might break any time!
|
## XXX might break any time!
|
||||||
csrf_token= csrf_tok
|
csrf_token= csrf_tok
|
||||||
|
|
@ -276,44 +266,6 @@ async def guild_feed(gname: str):
|
||||||
|
|
||||||
return dict(guilds={f'{Snowflake(gu.id):l}': gj}, feed=feed)
|
return dict(guilds={f'{Snowflake(gu.id):l}': gj}, feed=feed)
|
||||||
|
|
||||||
|
|
||||||
## CREATE ##
|
|
||||||
|
|
||||||
class CreateIn(BaseModel):
|
|
||||||
title: str
|
|
||||||
content: str
|
|
||||||
privacy: int = Field(default=0, ge=0, lt=4)
|
|
||||||
|
|
||||||
@bp.post('/guild/@<gname>')
|
|
||||||
@login_required
|
|
||||||
@validate_request(CreateIn)
|
|
||||||
async def guild_post(data: CreateIn, gname: str):
|
|
||||||
async with db as session:
|
|
||||||
user = current_user.user
|
|
||||||
gu: Guild | None = (await session.execute(select(Guild).where(Guild.name == gname))).scalar()
|
|
||||||
|
|
||||||
if gu is None:
|
|
||||||
return dict(error='Not found'), 404
|
|
||||||
if await gu.has_exiled(current_user.user):
|
|
||||||
return dict(error=f'You are banned from +{gname}'), 403
|
|
||||||
if not await gu.allows_posting(current_user.user):
|
|
||||||
return dict(error=f'You can\'t post on +{gname}'), 403
|
|
||||||
|
|
||||||
try:
|
|
||||||
new_post_id: int = (await session.execute(insert(Post).values(
|
|
||||||
author_id = user.id,
|
|
||||||
topic_id = gu.id,
|
|
||||||
privacy = data.privacy,
|
|
||||||
title = data.title,
|
|
||||||
text_content = data.text
|
|
||||||
).returning(Post.id))).scalar()
|
|
||||||
|
|
||||||
session.commit()
|
|
||||||
return dict(id=Snowflake(new_post_id).to_b32l()), 200
|
|
||||||
except Exception:
|
|
||||||
sys.excepthook(*sys.exc_info())
|
|
||||||
return {'error': 'Internal Server Error'}, 500
|
|
||||||
|
|
||||||
## LOGIN/OUT ##
|
## LOGIN/OUT ##
|
||||||
|
|
||||||
class LoginIn(BaseModel):
|
class LoginIn(BaseModel):
|
||||||
|
|
|
||||||
|
|
@ -13,9 +13,6 @@
|
||||||
<a href="?reason={{ opt.code |urlencode }}">{{ opt.description }}</a>
|
<a href="?reason={{ opt.code |urlencode }}">{{ opt.description }}</a>
|
||||||
</li>
|
</li>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
<li class="faint">
|
|
||||||
<a href="{{ back_to_url or 'javascript:history.go(-1);' }}">I clicked "Report" by mistake</a>
|
|
||||||
</li>
|
|
||||||
</ul>
|
</ul>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -13,9 +13,6 @@
|
||||||
<a href="?reason={{ opt.code |urlencode }}">{{ opt.description }}</a>
|
<a href="?reason={{ opt.code |urlencode }}">{{ opt.description }}</a>
|
||||||
</li>
|
</li>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
<li class="faint">
|
|
||||||
<a href="{{ back_to_url or 'javascript:history.go(-1);' }}">I clicked "Report" by mistake</a>
|
|
||||||
</li>
|
|
||||||
</ul>
|
</ul>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,19 +1,19 @@
|
||||||
|
|
||||||
# Terms of Service
|
# Terms of Service
|
||||||
|
|
||||||
This is a non-authoritative copy of the actual Terms, always updated at <https://ndspir.it/terms.html>.
|
This is a non-authoritative copy of the actual Terms, always updated at <https://yusur.moe/policies/terms.html>.
|
||||||
|
|
||||||
The following documents are incorporated into these Terms by reference
|
The following documents are incorporated into these Terms by reference
|
||||||
(i.e. an extension to these Terms in force):
|
(i.e. an extension to these Terms in force):
|
||||||
|
|
||||||
* [Privacy Policy](/privacy)
|
* [Privacy Policy](/privacy)
|
||||||
* [Community Guidelines](/rules)
|
* [Community Guidelines](/rules)
|
||||||
* [User Generated Content Terms](https://ndspir.it/ugc.html) on newdigitalspirit.com
|
* [User Generated Content Terms](https://yusur.moe/policies/ugc.html) on newdigitalspirit.com
|
||||||
* [Minors' Account Policy](https://ndspir.it/u18.html) on newdigitalspirit.com
|
* [Minors' Account Policy](https://yusur.moe/policies/u18.html) on newdigitalspirit.com
|
||||||
|
|
||||||
## Scope and Definition
|
## Scope and Definition
|
||||||
|
|
||||||
These terms of service ("Terms") are between **{{ app_name }}** and You,
|
These terms of service ("Terms") are between **New Digital Spirit** and You,
|
||||||
regarding Your use of all sites and services belonging to New Digital Spirit ("New Digital Spirit Network" / "the Services"),
|
regarding Your use of all sites and services belonging to New Digital Spirit ("New Digital Spirit Network" / "the Services"),
|
||||||
listed in detail in [Privacy Policy](/policies/privacy.html).
|
listed in detail in [Privacy Policy](/policies/privacy.html).
|
||||||
|
|
||||||
|
|
@ -21,27 +21,27 @@ Other websites are not covered by these Terms.
|
||||||
|
|
||||||
## Age
|
## Age
|
||||||
|
|
||||||
The whole of {{ app_name }} is PG-13. You may not use the Services if you are younger than 13 years old.
|
The whole of New Digital Spirit Network is PG-13. You may not use the Services if you are younger than 13 years old.
|
||||||
|
|
||||||
Additionally, you may not directly contact {{ app_name }} if you are younger than 18 years old, for any reason besides
|
Additionally, you may not directly contact New Digital Spirit if you are younger than 18 years old, for any reason besides
|
||||||
privacy-related requests. Any contact request knowingly from people younger than 18 will be ignored.
|
privacy-related requests. Any contact request knowingly from people younger than 18 will be ignored.
|
||||||
|
|
||||||
United States resident under the age of 18 are **not allowed** in any way to access our network without logging in.
|
United States resident under the age of 18 are **not allowed** in any way to access our network without logging in.
|
||||||
|
|
||||||
New Digital Spirit reserves the right to require ID verification in case of age doubt or suspected security threat.
|
New Digital Spirit reserves the right to require ID verification in case of age doubt or potential security threat.
|
||||||
|
|
||||||
Minors on New Digital Spirit Network are additionally bound to the [Minor Account Policy](https://ndspir.it/u18.html),
|
Minors on New Digital Spirit Network are additionally bound to the [Minor Account Policy](/policies/u18.html),
|
||||||
incorporated here by reference.
|
incorporated here by reference.
|
||||||
|
|
||||||
Systems and plurals are considered to be minors, no matter their body age.
|
Systems and plurals are considered to be minors, no matter their body age.
|
||||||
|
|
||||||
## Intellectual property
|
## Intellectual property
|
||||||
|
|
||||||
Except otherwise noted, the entirety of the content on {{ app_name }}
|
Except otherwise noted, the entirety of the content on the New Digital Spirit Network
|
||||||
is intellectual property of {{ app_name }}. All rights reserved.
|
is intellectual property of Sakuragasaki46 and New Digital Spirit. All rights reserved.
|
||||||
|
|
||||||
You may not copy, modify, redistribute, mirror the contents of or create alternative Service to
|
You may not copy, modify, redistribute, mirror the contents of or create alternative Service to
|
||||||
{{ server_name }} or any other of the Services, or portions thereof, without {{ app_name }}'s
|
yusur.moe or any other of the Services, or portions thereof, without New Digital Spirit's
|
||||||
prior written permission.
|
prior written permission.
|
||||||
|
|
||||||
## Privacy Rights
|
## Privacy Rights
|
||||||
|
|
@ -53,7 +53,7 @@ identification or damages to Sakuragasaki46's private life.
|
||||||
Disclosure will be legally regarded as a violation of privacy and a breach of
|
Disclosure will be legally regarded as a violation of privacy and a breach of
|
||||||
non-disclosure agreement (NDA), and will be acted upon accordingly, regardless of
|
non-disclosure agreement (NDA), and will be acted upon accordingly, regardless of
|
||||||
the infringer's age or any other legal protection, included but not limited to
|
the infringer's age or any other legal protection, included but not limited to
|
||||||
termination of the infringer's accounts.
|
termination of the infringer,s accounts.
|
||||||
|
|
||||||
## IP Loggers
|
## IP Loggers
|
||||||
|
|
||||||
|
|
@ -65,11 +65,11 @@ legitimate interest. Logged information contains user agent strings as well.
|
||||||
## User Generated Content
|
## User Generated Content
|
||||||
|
|
||||||
Some of our Services allow user generated content. By using them, you agree to be bound
|
Some of our Services allow user generated content. By using them, you agree to be bound
|
||||||
to the [User Generated Content Terms](https://ndspir.it/ugc.html), incorporated here by reference.
|
to the [User Generated Content Terms](/policies/ugc.html), incorporated here by reference.
|
||||||
|
|
||||||
## No Warranty
|
## No Warranty
|
||||||
|
|
||||||
**Except as represented in this agreement, {{ app_name }}
|
**Except as represented in this agreement, the New Digital Spirit Network
|
||||||
is provided “AS IS”. Other than as provided in this agreement,
|
is provided “AS IS”. Other than as provided in this agreement,
|
||||||
New Digital Spirit makes no other warranties, express or implied, and hereby
|
New Digital Spirit makes no other warranties, express or implied, and hereby
|
||||||
disclaims all implied warranties, including any warranty of merchantability
|
disclaims all implied warranties, including any warranty of merchantability
|
||||||
|
|
@ -77,13 +77,13 @@ and warranty of fitness for a particular purpose.**
|
||||||
|
|
||||||
## Liability
|
## Liability
|
||||||
|
|
||||||
{{ app_name }} **shall not be accountable** for Your damages arising from Your use
|
Sakuragasaki46 or New Digital Spirit **shall not be accountable** for Your damages arising from Your use
|
||||||
of the New Digital Spirit Network.
|
of the New Digital Spirit Network.
|
||||||
|
|
||||||
## Indemnify
|
## Indemnify
|
||||||
|
|
||||||
You agree to [indemnify and hold harmless](https://www.upcounsel.com/difference-between-indemnify-and-hold-harmless)
|
You agree to [indemnify and hold harmless](https://www.upcounsel.com/difference-between-indemnify-and-hold-harmless)
|
||||||
{{ app_name }} from any and all claims, damages, liabilities, costs and expenses, including reasonable and unreasonable
|
Sakuragasaki46 and New Digital Spirit from any and all claims, damages, liabilities, costs and expenses, including reasonable and unreasonable
|
||||||
counsel and attorney’s fees, arising out of any breach of this agreement.
|
counsel and attorney’s fees, arising out of any breach of this agreement.
|
||||||
|
|
||||||
## Severability
|
## Severability
|
||||||
|
|
@ -95,7 +95,7 @@ according to the governing law, the remainder of these Terms shall remain in pla
|
||||||
|
|
||||||
These terms of services are governed by, and shall be interpreted in accordance
|
These terms of services are governed by, and shall be interpreted in accordance
|
||||||
with, the laws of Italy. You consent to the sole jurisdiction of \[REDACTED], Italy
|
with, the laws of Italy. You consent to the sole jurisdiction of \[REDACTED], Italy
|
||||||
for all disputes between You and {{ app_name }}, and You consent to the sole
|
for all disputes between You and , and You consent to the sole
|
||||||
application of Italian law and European Union law for all such disputes.
|
application of Italian law and European Union law for all such disputes.
|
||||||
|
|
||||||
## Updates
|
## Updates
|
||||||
|
|
|
||||||
|
|
@ -103,8 +103,7 @@ async def accept_report(target, source: PostReport):
|
||||||
await remove_content(target, source.reason_code)
|
await remove_content(target, source.reason_code)
|
||||||
|
|
||||||
source.update_status = REPORT_UPDATE_COMPLETE
|
source.update_status = REPORT_UPDATE_COMPLETE
|
||||||
# XXX disabled because of a session conflict
|
session.add(source)
|
||||||
#session.add(source)
|
|
||||||
|
|
||||||
|
|
||||||
@additem(REPORT_ACTIONS, '2')
|
@additem(REPORT_ACTIONS, '2')
|
||||||
|
|
@ -128,21 +127,21 @@ async def strike_report(target, source: PostReport):
|
||||||
author.banned_reason = source.reason_code
|
author.banned_reason = source.reason_code
|
||||||
|
|
||||||
source.update_status = REPORT_UPDATE_COMPLETE
|
source.update_status = REPORT_UPDATE_COMPLETE
|
||||||
#session.add(source)
|
session.add(source)
|
||||||
|
|
||||||
|
|
||||||
@additem(REPORT_ACTIONS, '0')
|
@additem(REPORT_ACTIONS, '0')
|
||||||
async def reject_report(target, source: PostReport):
|
async def reject_report(target, source: PostReport):
|
||||||
async with db as session:
|
async with db as session:
|
||||||
source.update_status = REPORT_UPDATE_REJECTED
|
source.update_status = REPORT_UPDATE_REJECTED
|
||||||
#session.add(source)
|
session.add(source)
|
||||||
|
|
||||||
|
|
||||||
@additem(REPORT_ACTIONS, '3')
|
@additem(REPORT_ACTIONS, '3')
|
||||||
async def withhold_report(target, source: PostReport):
|
async def withhold_report(target, source: PostReport):
|
||||||
async with db as session:
|
async with db as session:
|
||||||
source.update_status = REPORT_UPDATE_ON_HOLD
|
source.update_status = REPORT_UPDATE_ON_HOLD
|
||||||
#session.add(source)
|
session.add(source)
|
||||||
|
|
||||||
|
|
||||||
@additem(REPORT_ACTIONS, '4')
|
@additem(REPORT_ACTIONS, '4')
|
||||||
|
|
@ -197,7 +196,7 @@ async def strikes():
|
||||||
@bp.route('/admin/users/')
|
@bp.route('/admin/users/')
|
||||||
@admin_required
|
@admin_required
|
||||||
async def users():
|
async def users():
|
||||||
user_list = await db.paginate(select(User).order_by(User.joined_at.desc()), page=int(request.args.get('page', 1)))
|
user_list = await db.paginate(select(User).order_by(User.joined_at.desc()))
|
||||||
return await render_template('admin/admin_users.html',
|
return await render_template('admin/admin_users.html',
|
||||||
user_list=user_list, account_status_string=colorized_account_status_string)
|
user_list=user_list, account_status_string=colorized_account_status_string)
|
||||||
|
|
||||||
|
|
@ -205,7 +204,7 @@ async def users():
|
||||||
@admin_required
|
@admin_required
|
||||||
async def user_detail(id: int):
|
async def user_detail(id: int):
|
||||||
async with db as session:
|
async with db as session:
|
||||||
u = (await session.execute(select(User).where(User.id == id))).scalar()
|
u = session.execute(select(User).where(User.id == id)).scalar()
|
||||||
if u is None:
|
if u is None:
|
||||||
abort(404)
|
abort(404)
|
||||||
if request.method == 'POST':
|
if request.method == 'POST':
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue