2018-07-04 19:08:45 +00:00
|
|
|
import mimetypes
|
2018-05-18 18:41:41 +00:00
|
|
|
import os
|
2018-06-16 20:02:10 +00:00
|
|
|
import subprocess
|
|
|
|
from datetime import datetime
|
2018-07-01 09:40:44 +00:00
|
|
|
from enum import Enum
|
2018-06-16 20:02:10 +00:00
|
|
|
|
2019-04-22 07:58:11 +00:00
|
|
|
import pymongo
|
2018-05-18 18:41:41 +00:00
|
|
|
import requests
|
2018-07-01 09:40:44 +00:00
|
|
|
import sass
|
2018-07-01 10:49:40 +00:00
|
|
|
import yaml
|
2018-05-30 21:47:01 +00:00
|
|
|
from itsdangerous import JSONWebSignatureSerializer
|
2018-07-11 21:22:47 +00:00
|
|
|
from little_boxes import strtobool
|
2018-07-22 19:34:42 +00:00
|
|
|
from little_boxes.activitypub import DEFAULT_CTX
|
2018-06-16 20:02:10 +00:00
|
|
|
from pymongo import MongoClient
|
2018-05-18 18:41:41 +00:00
|
|
|
|
2018-06-16 20:02:10 +00:00
|
|
|
from utils.key import KEY_DIR
|
|
|
|
from utils.key import get_key
|
|
|
|
from utils.key import get_secret_key
|
2018-07-06 21:15:49 +00:00
|
|
|
from utils.media import MediaCache
|
2018-06-16 19:24:53 +00:00
|
|
|
|
2018-05-18 18:41:41 +00:00
|
|
|
|
2018-07-01 09:40:44 +00:00
|
|
|
class ThemeStyle(Enum):
|
|
|
|
LIGHT = "light"
|
|
|
|
DARK = "dark"
|
|
|
|
|
|
|
|
|
|
|
|
DEFAULT_THEME_STYLE = ThemeStyle.LIGHT.value
|
|
|
|
|
|
|
|
DEFAULT_THEME_PRIMARY_COLOR = {
|
|
|
|
ThemeStyle.LIGHT: "#1d781d", # Green
|
2018-07-01 10:49:16 +00:00
|
|
|
ThemeStyle.DARK: "#33ff00", # Purple
|
2018-07-01 09:40:44 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-05-21 15:04:53 +00:00
|
|
|
def noop():
|
|
|
|
pass
|
2018-05-18 18:41:41 +00:00
|
|
|
|
2018-05-21 15:04:53 +00:00
|
|
|
|
|
|
|
CUSTOM_CACHE_HOOKS = False
|
|
|
|
try:
|
2018-06-16 19:24:53 +00:00
|
|
|
from cache_hooks import purge as custom_cache_purge_hook
|
2018-05-21 15:04:53 +00:00
|
|
|
except ModuleNotFoundError:
|
|
|
|
custom_cache_purge_hook = noop
|
|
|
|
|
2018-06-17 17:21:59 +00:00
|
|
|
VERSION = (
|
|
|
|
subprocess.check_output(["git", "describe", "--always"]).split()[0].decode("utf-8")
|
|
|
|
)
|
2018-05-18 18:41:41 +00:00
|
|
|
|
2018-06-17 17:21:59 +00:00
|
|
|
DEBUG_MODE = strtobool(os.getenv("MICROBLOGPUB_DEBUG", "false"))
|
2018-05-27 09:01:34 +00:00
|
|
|
|
2018-05-18 18:41:41 +00:00
|
|
|
HEADERS = [
|
2018-06-17 17:21:59 +00:00
|
|
|
"application/activity+json",
|
|
|
|
"application/ld+json;profile=https://www.w3.org/ns/activitystreams",
|
2018-05-18 18:41:41 +00:00
|
|
|
'application/ld+json; profile="https://www.w3.org/ns/activitystreams"',
|
2018-06-17 17:21:59 +00:00
|
|
|
"application/ld+json",
|
2018-05-18 18:41:41 +00:00
|
|
|
]
|
|
|
|
|
|
|
|
|
2018-06-17 17:21:59 +00:00
|
|
|
with open(os.path.join(KEY_DIR, "me.yml")) as f:
|
2018-05-18 18:41:41 +00:00
|
|
|
conf = yaml.load(f)
|
|
|
|
|
2018-06-17 17:21:59 +00:00
|
|
|
USERNAME = conf["username"]
|
|
|
|
NAME = conf["name"]
|
|
|
|
DOMAIN = conf["domain"]
|
|
|
|
SCHEME = "https" if conf.get("https", True) else "http"
|
|
|
|
BASE_URL = SCHEME + "://" + DOMAIN
|
2018-05-18 18:41:41 +00:00
|
|
|
ID = BASE_URL
|
2018-06-17 17:21:59 +00:00
|
|
|
SUMMARY = conf["summary"]
|
|
|
|
ICON_URL = conf["icon_url"]
|
|
|
|
PASS = conf["pass"]
|
2018-07-07 11:18:01 +00:00
|
|
|
EXTRA_INBOXES = conf.get("extra_inboxes", [])
|
2018-07-01 09:40:44 +00:00
|
|
|
|
2018-07-17 22:29:45 +00:00
|
|
|
HIDE_FOLLOWING = conf.get("hide_following", True)
|
2018-07-17 21:42:21 +00:00
|
|
|
|
2018-07-01 09:40:44 +00:00
|
|
|
# Theme-related config
|
|
|
|
theme_conf = conf.get("theme", {})
|
|
|
|
THEME_STYLE = ThemeStyle(theme_conf.get("style", DEFAULT_THEME_STYLE))
|
|
|
|
THEME_COLOR = theme_conf.get("color", DEFAULT_THEME_PRIMARY_COLOR[THEME_STYLE])
|
|
|
|
|
|
|
|
|
|
|
|
SASS_DIR = os.path.join(os.path.dirname(os.path.abspath(__file__)), "sass")
|
|
|
|
theme_css = f"$primary-color: {THEME_COLOR};\n"
|
|
|
|
with open(os.path.join(SASS_DIR, f"{THEME_STYLE.value}.scss")) as f:
|
|
|
|
theme_css += f.read()
|
2018-07-01 10:49:40 +00:00
|
|
|
theme_css += "\n"
|
2018-07-01 09:40:44 +00:00
|
|
|
with open(os.path.join(SASS_DIR, "base_theme.scss")) as f:
|
|
|
|
raw_css = theme_css + f.read()
|
2018-07-04 19:08:45 +00:00
|
|
|
CSS = sass.compile(string=raw_css, output_style="compressed")
|
2018-07-01 09:40:44 +00:00
|
|
|
|
2018-05-18 18:41:41 +00:00
|
|
|
|
|
|
|
USER_AGENT = (
|
2018-06-22 22:29:06 +00:00
|
|
|
f"{requests.utils.default_user_agent()} (microblog.pub/{VERSION}; +{BASE_URL})"
|
2018-05-18 18:41:41 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
mongo_client = MongoClient(
|
2018-06-17 17:21:59 +00:00
|
|
|
host=[os.getenv("MICROBLOGPUB_MONGODB_HOST", "localhost:27017")]
|
2018-05-18 18:41:41 +00:00
|
|
|
)
|
|
|
|
|
2018-06-17 17:21:59 +00:00
|
|
|
DB_NAME = "{}_{}".format(USERNAME, DOMAIN.replace(".", "_"))
|
2018-05-27 09:50:09 +00:00
|
|
|
DB = mongo_client[DB_NAME]
|
2018-07-04 23:02:51 +00:00
|
|
|
GRIDFS = mongo_client[f"{DB_NAME}_gridfs"]
|
2018-07-06 21:15:49 +00:00
|
|
|
MEDIA_CACHE = MediaCache(GRIDFS, USER_AGENT)
|
2018-05-27 09:50:09 +00:00
|
|
|
|
2018-06-16 19:24:53 +00:00
|
|
|
|
2018-09-01 09:17:46 +00:00
|
|
|
def create_indexes():
|
2019-04-09 19:32:16 +00:00
|
|
|
DB.command("compact", "activities")
|
2018-09-01 09:17:46 +00:00
|
|
|
DB.activities.create_index([("remote_id", pymongo.ASCENDING)])
|
2018-09-01 09:38:54 +00:00
|
|
|
DB.activities.create_index([("activity.object.id", pymongo.ASCENDING)])
|
2019-04-10 20:50:36 +00:00
|
|
|
DB.activities.create_index([("meta.thread_root_parent", pymongo.ASCENDING)])
|
|
|
|
DB.activities.create_index(
|
2019-04-13 08:00:56 +00:00
|
|
|
[
|
|
|
|
("meta.thread_root_parent", pymongo.ASCENDING),
|
|
|
|
("meta.deleted", pymongo.ASCENDING),
|
|
|
|
]
|
2019-04-10 20:50:36 +00:00
|
|
|
)
|
2019-04-05 09:35:48 +00:00
|
|
|
DB.activities.create_index(
|
|
|
|
[("activity.object.id", pymongo.ASCENDING), ("meta.deleted", pymongo.ASCENDING)]
|
|
|
|
)
|
|
|
|
DB.cache2.create_index(
|
|
|
|
[
|
|
|
|
("path", pymongo.ASCENDING),
|
|
|
|
("type", pymongo.ASCENDING),
|
|
|
|
("arg", pymongo.ASCENDING),
|
|
|
|
]
|
|
|
|
)
|
|
|
|
DB.cache2.create_index("date", expireAfterSeconds=3600 * 12)
|
2018-09-01 09:17:46 +00:00
|
|
|
|
|
|
|
# Index for the block query
|
|
|
|
DB.activities.create_index(
|
|
|
|
[
|
|
|
|
("box", pymongo.ASCENDING),
|
|
|
|
("type", pymongo.ASCENDING),
|
|
|
|
("meta.undo", pymongo.ASCENDING),
|
|
|
|
]
|
|
|
|
)
|
|
|
|
|
|
|
|
# Index for count queries
|
|
|
|
DB.activities.create_index(
|
|
|
|
[
|
|
|
|
("box", pymongo.ASCENDING),
|
|
|
|
("type", pymongo.ASCENDING),
|
|
|
|
("meta.undo", pymongo.ASCENDING),
|
|
|
|
("meta.deleted", pymongo.ASCENDING),
|
|
|
|
]
|
|
|
|
)
|
|
|
|
|
2019-04-14 17:17:54 +00:00
|
|
|
DB.activities.create_index([("box", pymongo.ASCENDING)])
|
|
|
|
|
|
|
|
# Outbox query
|
|
|
|
DB.activities.create_index(
|
|
|
|
[
|
|
|
|
("box", pymongo.ASCENDING),
|
|
|
|
("type", pymongo.ASCENDING),
|
|
|
|
("meta.undo", pymongo.ASCENDING),
|
|
|
|
("meta.deleted", pymongo.ASCENDING),
|
|
|
|
("meta.public", pymongo.ASCENDING),
|
|
|
|
]
|
|
|
|
)
|
|
|
|
|
2018-09-01 09:17:46 +00:00
|
|
|
DB.activities.create_index(
|
|
|
|
[
|
|
|
|
("type", pymongo.ASCENDING),
|
|
|
|
("activity.object.type", pymongo.ASCENDING),
|
|
|
|
("activity.object.inReplyTo", pymongo.ASCENDING),
|
|
|
|
("meta.deleted", pymongo.ASCENDING),
|
|
|
|
]
|
|
|
|
)
|
|
|
|
|
|
|
|
|
2018-05-27 09:50:09 +00:00
|
|
|
def _drop_db():
|
|
|
|
if not DEBUG_MODE:
|
|
|
|
return
|
|
|
|
|
|
|
|
mongo_client.drop_database(DB_NAME)
|
|
|
|
|
2018-06-16 19:24:53 +00:00
|
|
|
|
2019-04-17 21:36:28 +00:00
|
|
|
KEY = get_key(ID, ID + "#main-key", USERNAME, DOMAIN)
|
2018-05-18 18:41:41 +00:00
|
|
|
|
2018-05-30 21:47:01 +00:00
|
|
|
|
2018-06-17 17:21:59 +00:00
|
|
|
JWT_SECRET = get_secret_key("jwt")
|
2018-05-30 21:47:01 +00:00
|
|
|
JWT = JSONWebSignatureSerializer(JWT_SECRET)
|
|
|
|
|
2018-06-16 19:24:53 +00:00
|
|
|
|
2018-05-30 21:47:01 +00:00
|
|
|
def _admin_jwt_token() -> str:
|
2018-06-18 20:04:24 +00:00
|
|
|
return JWT.dumps( # type: ignore
|
2018-06-17 20:05:38 +00:00
|
|
|
{"me": "ADMIN", "ts": datetime.now().timestamp()}
|
|
|
|
).decode( # type: ignore
|
2018-06-17 17:21:59 +00:00
|
|
|
"utf-8"
|
2018-06-17 18:51:23 +00:00
|
|
|
)
|
2018-05-30 21:47:01 +00:00
|
|
|
|
|
|
|
|
2018-06-17 17:21:59 +00:00
|
|
|
ADMIN_API_KEY = get_secret_key("admin_api_key", _admin_jwt_token)
|
2018-05-30 21:47:01 +00:00
|
|
|
|
2018-05-18 18:41:41 +00:00
|
|
|
ME = {
|
2018-07-22 19:34:42 +00:00
|
|
|
"@context": DEFAULT_CTX,
|
2018-05-18 18:41:41 +00:00
|
|
|
"type": "Person",
|
|
|
|
"id": ID,
|
2018-06-17 17:21:59 +00:00
|
|
|
"following": ID + "/following",
|
|
|
|
"followers": ID + "/followers",
|
2018-07-22 19:54:24 +00:00
|
|
|
"featured": ID + "/featured",
|
2018-06-17 17:21:59 +00:00
|
|
|
"liked": ID + "/liked",
|
|
|
|
"inbox": ID + "/inbox",
|
|
|
|
"outbox": ID + "/outbox",
|
2018-05-18 18:41:41 +00:00
|
|
|
"preferredUsername": USERNAME,
|
|
|
|
"name": NAME,
|
|
|
|
"summary": SUMMARY,
|
|
|
|
"endpoints": {},
|
|
|
|
"url": ID,
|
2018-07-24 22:02:46 +00:00
|
|
|
"manuallyApprovesFollowers": False,
|
|
|
|
"attachment": [],
|
2018-07-04 19:08:45 +00:00
|
|
|
"icon": {
|
|
|
|
"mediaType": mimetypes.guess_type(ICON_URL)[0],
|
|
|
|
"type": "Image",
|
|
|
|
"url": ICON_URL,
|
|
|
|
},
|
2018-06-16 19:24:53 +00:00
|
|
|
"publicKey": KEY.to_dict(),
|
2018-05-18 18:41:41 +00:00
|
|
|
}
|
2019-04-10 20:50:36 +00:00
|
|
|
|
|
|
|
# TODO(tsileo): read the config from the YAML if set
|
2019-04-10 21:08:38 +00:00
|
|
|
EMOJIS = "😺 😸 😹 😻 😼 😽 🙀 😿 😾"
|