Move all instance files to instance directory

Still to move: thumbnails
This commit is contained in:
augustin64 2024-01-16 18:50:19 +01:00
parent f43b1e1090
commit 6f5031623c
13 changed files with 162 additions and 73 deletions

View File

@ -19,3 +19,8 @@ BASE_URL="http://localhost:5000"
# Session expiration, in days # Session expiration, in days
MAX_AGE=31 MAX_AGE=31
# Instance path ie. where are all the files + the database stored
# Keep in mind that this config option can only be loaded from default_config.py,
# as the custom config is stored in $INSTANCE_PATH/
INSTANCE_PATH="instance"

28
make.sh
View File

@ -1,25 +1,27 @@
#!/bin/bash #!/bin/bash
init () { INSTANCE_PATH="instance"
mkdir -p "instance"
mkdir -p "partitioncloud/partitions"
mkdir -p "partitioncloud/attachments"
mkdir -p "partitioncloud/search-partitions"
mkdir -p "partitioncloud/static/thumbnails"
mkdir -p "partitioncloud/static/search-thumbnails"
if ! test -f "instance/config.py"; then init () {
echo "SECRET_KEY=$(python3 -c 'import secrets; print(secrets.token_hex())')" > instance/config.py mkdir -p "$INSTANCE_PATH"
mkdir -p "$INSTANCE_PATH/partitions"
mkdir -p "$INSTANCE_PATH/attachments"
mkdir -p "$INSTANCE_PATH/search-partitions"
mkdir -p "$INSTANCE_PATH/static/thumbnails"
mkdir -p "$INSTANCE_PATH/static/search-thumbnails"
if ! test -f "$INSTANCE_PATH/config.py"; then
echo "SECRET_KEY=$(python3 -c 'import secrets; print(secrets.token_hex())')" > "$INSTANCE_PATH/config.py"
fi fi
if test -f "instance/partitioncloud.sqlite"; then if test -f "$INSTANCE_PATH/partitioncloud.sqlite"; then
printf "Souhaitez vous supprimer la base de données existante ? [y/n] " printf "Souhaitez vous supprimer la base de données existante ? [y/n] "
read -r CONFIRMATION read -r CONFIRMATION
[[ $CONFIRMATION == y ]] || exit 1 [[ $CONFIRMATION == y ]] || exit 1
fi fi
sqlite3 "instance/partitioncloud.sqlite" '.read partitioncloud/schema.sql' sqlite3 "$INSTANCE_PATH/partitioncloud.sqlite" '.read partitioncloud/schema.sql'
echo "Base de données créé" echo "Base de données créé"
sqlite3 "instance/partitioncloud.sqlite" '.read partitioncloud/init.sql' sqlite3 "$INSTANCE_PATH/partitioncloud.sqlite" '.read partitioncloud/init.sql'
echo "Utilisateur root:root ajouté" echo "Utilisateur root:root ajouté"
} }
@ -43,7 +45,7 @@ usage () {
if [[ $1 && $(type "$1") = *"is a"*"function"* || $(type "$1") == *"est une fonction"* ]]; then if [[ $1 && $(type "$1") = *"is a"*"function"* || $(type "$1") == *"est une fonction"* ]]; then
# Import config # Import config
source "default_config.py" source "default_config.py"
[[ ! -x instance/config.py ]] && source "instance/config.py" [[ ! -x" $INSTANCE_PATH/config.py" ]] && source "$INSTANCE_PATH/config.py"
$1 ${*:2} # Call the function $1 ${*:2} # Call the function
else else
usage usage

View File

@ -3,8 +3,10 @@
Main file Main file
""" """
import os import os
import sys
import datetime import datetime
import subprocess import subprocess
import importlib.util
from flask import Flask, g, redirect, render_template, request, send_file, flash, session, abort from flask import Flask, g, redirect, render_template, request, send_file, flash, session, abort
from werkzeug.security import generate_password_hash from werkzeug.security import generate_password_hash
@ -16,14 +18,48 @@ from .modules.db import get_db
app = Flask(__name__) app = Flask(__name__)
def load_config():
app.config.from_object('default_config')
app.instance_path = os.path.abspath(app.config["INSTANCE_PATH"])
if not os.path.exists(app.instance_path):
print("[ERROR] Instance path does not exist. Make sure to use an existing directory.")
sys.exit(1)
if os.path.exists(f"{app.instance_path}/config.py"):
# Load module from instance_path/config.py in user_config object
spec = importlib.util.spec_from_file_location(
".",
os.path.join(app.instance_path, "config.py")
)
user_config = importlib.util.module_from_spec(spec)
spec.loader.exec_module(user_config)
app.config.from_object(user_config)
if os.path.abspath(app.config["INSTANCE_PATH"]) != app.instance_path:
print("[ERROR] Using two different instance path. \
\nPlease modify INSTANCE_PATH only in default_config.py and remove it from $INSTANCE_PATH/config.py")
sys.exit(1)
else:
print("[WARNING] Using default config")
app.config.from_mapping( app.config.from_mapping(
DATABASE=os.path.join(app.instance_path, f"{__name__}.sqlite"), DATABASE=os.path.join(app.instance_path, f"{__name__}.sqlite"),
) )
app.config.from_object('default_config')
if os.path.exists("instance/config.py"):
app.config.from_object('instance.config') def get_version():
else: try:
print("[WARNING] Using default config") result = subprocess.run(["git", "describe", "--tags"], stdout=subprocess.PIPE, check=True)
return result.stdout.decode('utf8')
except (FileNotFoundError, subprocess.CalledProcessError):
# In case git not found or any platform specific weird error
return "unknown"
load_config()
app.register_blueprint(auth.bp) app.register_blueprint(auth.bp)
app.register_blueprint(admin.bp) app.register_blueprint(admin.bp)
@ -31,13 +67,7 @@ app.register_blueprint(groupe.bp)
app.register_blueprint(albums.bp) app.register_blueprint(albums.bp)
app.register_blueprint(partition.bp) app.register_blueprint(partition.bp)
__version__ = get_version()
try:
result = subprocess.run(["git", "describe", "--tags"], stdout=subprocess.PIPE, check=True)
__version__ = result.stdout.decode('utf8')
except (FileNotFoundError, subprocess.CalledProcessError):
# In case git not found or any platform specific weird error
__version__ = "unknown"
@app.route("/") @app.route("/")
@ -99,7 +129,7 @@ def search_thumbnail(uuid):
f'/usr/bin/convert -thumbnail\ f'/usr/bin/convert -thumbnail\
"178^>" -background white -alpha \ "178^>" -background white -alpha \
remove -crop 178x178+0+0 \ remove -crop 178x178+0+0 \
partitioncloud/search-partitions/{uuid}.pdf[0] \ {app.instance_path}/search-partitions/{uuid}.pdf[0] \
partitioncloud/static/search-thumbnails/{uuid}.jpg' partitioncloud/static/search-thumbnails/{uuid}.jpg'
) )

View File

@ -42,7 +42,7 @@ def search_page():
query = request.form["query"] query = request.form["query"]
nb_queries = abs(int(request.form["nb-queries"])) nb_queries = abs(int(request.form["nb-queries"]))
search.flush_cache() search.flush_cache(current_app.instance_path)
partitions_local = search.local_search(query, utils.get_all_partitions()) partitions_local = search.local_search(query, utils.get_all_partitions())
user = User(user_id=session.get("user_id")) user = User(user_id=session.get("user_id"))
@ -52,7 +52,7 @@ def search_page():
nb_queries = min(current_app.config["MAX_ONLINE_QUERIES"], nb_queries) nb_queries = min(current_app.config["MAX_ONLINE_QUERIES"], nb_queries)
else: else:
nb_queries = min(10, nb_queries) # Query limit is 10 for an admin nb_queries = min(10, nb_queries) # Query limit is 10 for an admin
google_results = search.online_search(query, nb_queries) google_results = search.online_search(query, nb_queries, current_app.instance_path)
else: else:
google_results = [] google_results = []
@ -207,7 +207,7 @@ def delete_album(uuid):
flash(error) flash(error)
return redirect(request.referrer) return redirect(request.referrer)
album.delete() album.delete(current_app.instance_path)
flash("Album supprimé.") flash("Album supprimé.")
return redirect("/albums") return redirect("/albums")
@ -278,20 +278,32 @@ def add_partition(album_uuid):
) )
db.commit() db.commit()
partition_path = os.path.join(
current_app.instance_path,
"partitions",
f"{partition_uuid}.pdf"
)
if partition_type == "file": if partition_type == "file":
file = request.files["file"] file = request.files["file"]
file.save(f"partitioncloud/partitions/{partition_uuid}.pdf") file.save(partition_path)
else: else:
search_partition_path = os.path.join(
current_app.instance_path,
"search-partitions",
f"{search_uuid}.pdf"
)
shutil.copyfile( shutil.copyfile(
f"partitioncloud/search-partitions/{search_uuid}.pdf", search_partition_path,
f"partitioncloud/partitions/{partition_uuid}.pdf" partition_path
) )
os.system( os.system(
f'/usr/bin/convert -thumbnail\ f'/usr/bin/convert -thumbnail\
"178^>" -background white -alpha \ "178^>" -background white -alpha \
remove -crop 178x178+0+0 \ remove -crop 178x178+0+0 \
partitioncloud/partitions/{partition_uuid}.pdf[0] \ {partition_path}[0] \
partitioncloud/static/thumbnails/{partition_uuid}.jpg' partitioncloud/static/thumbnails/{partition_uuid}.jpg'
) )
db.commit() db.commit()

View File

@ -82,7 +82,7 @@ class Album():
).fetchall() ).fetchall()
def delete(self): def delete(self, instance_path):
""" """
Supprimer l'album Supprimer l'album
""" """
@ -130,9 +130,9 @@ class Album():
attachments = [Attachment(data=i) for i in data] attachments = [Attachment(data=i) for i in data]
for attachment in attachments: for attachment in attachments:
attachment.delete() attachment.delete(instance_path)
os.remove(f"partitioncloud/partitions/{partition['uuid']}.pdf") os.remove(f"{instance_path}/partitions/{partition['uuid']}.pdf")
if os.path.exists(f"partitioncloud/static/thumbnails/{partition['uuid']}.jpg"): if os.path.exists(f"partitioncloud/static/thumbnails/{partition['uuid']}.jpg"):
os.remove(f"partitioncloud/static/thumbnails/{partition['uuid']}.jpg") os.remove(f"partitioncloud/static/thumbnails/{partition['uuid']}.jpg")

View File

@ -29,7 +29,7 @@ class Attachment():
self.filetype = data["filetype"] self.filetype = data["filetype"]
self.partition_uuid = data["partition_uuid"] self.partition_uuid = data["partition_uuid"]
def delete(self): def delete(self, instance_path):
db = get_db() db = get_db()
db.execute( db.execute(
""" """
@ -40,7 +40,7 @@ class Attachment():
) )
db.commit() db.commit()
os.remove(f"partitioncloud/attachments/{self.uuid}.{self.filetype}") os.remove(f"{instance_path}/attachments/{self.uuid}.{self.filetype}")
def __repr__(self): def __repr__(self):
return f"{self.name}.{self.filetype}" return f"{self.name}.{self.filetype}"

View File

@ -21,7 +21,7 @@ class Groupe():
self.albums = None self.albums = None
self.admins = None self.admins = None
def delete(self): def delete(self, instance_path):
""" """
Supprime le groupe, et les albums laissés orphelins (sans utilisateur) Supprime le groupe, et les albums laissés orphelins (sans utilisateur)
""" """
@ -63,7 +63,7 @@ class Groupe():
for i in data: for i in data:
album = Album(id=i["id"]) album = Album(id=i["id"])
album.delete() album.delete(instance_path)
def get_users(self): def get_users(self):

View File

@ -29,7 +29,7 @@ class Partition():
else: else:
raise LookupError raise LookupError
def delete(self): def delete(self, instance_path):
self.load_attachments() self.load_attachments()
db = get_db() db = get_db()
@ -42,7 +42,7 @@ class Partition():
) )
db.commit() db.commit()
os.remove(f"partitioncloud/partitions/{self.uuid}.pdf") os.remove(f"{instance_path}/partitions/{self.uuid}.pdf")
if os.path.exists(f"partitioncloud/static/thumbnails/{self.uuid}.jpg"): if os.path.exists(f"partitioncloud/static/thumbnails/{self.uuid}.jpg"):
os.remove(f"partitioncloud/static/thumbnails/{self.uuid}.jpg") os.remove(f"partitioncloud/static/thumbnails/{self.uuid}.jpg")
@ -56,7 +56,7 @@ class Partition():
db.commit() db.commit()
for attachment in self.attachments: for attachment in self.attachments:
attachment.delete() attachment.delete(instance_path)
def update(self, name=None, author="", body=""): def update(self, name=None, author="", body=""):
if name is None: if name is None:

View File

@ -3,7 +3,7 @@
Groupe module Groupe module
""" """
from flask import (Blueprint, abort, flash, redirect, render_template, from flask import (Blueprint, abort, flash, redirect, render_template,
request, session) request, session, current_app)
from .auth import login_required from .auth import login_required
from .db import get_db from .db import get_db
@ -155,7 +155,7 @@ def delete_groupe(uuid):
flash(error) flash(error)
return redirect(request.referrer) return redirect(request.referrer)
groupe.delete() groupe.delete(current_app.instance_path)
flash("Groupe supprimé.") flash("Groupe supprimé.")
return redirect("/albums") return redirect("/albums")

View File

@ -4,7 +4,8 @@ Partition module
""" """
import os import os
from uuid import uuid4 from uuid import uuid4
from flask import Blueprint, abort, send_file, render_template, request, redirect, flash, session from flask import (Blueprint, abort, send_file, render_template,
request, redirect, flash, session, current_app)
from .db import get_db from .db import get_db
from .auth import login_required, admin_required from .auth import login_required, admin_required
@ -21,9 +22,11 @@ def get_partition(uuid):
abort(404) abort(404)
return send_file( return send_file(os.path.join(
os.path.join("partitions", f"{uuid}.pdf"), current_app.instance_path,
download_name = f"{partition.name}.pdf" "partitions",
f"{uuid}.pdf"
), download_name = f"{partition.name}.pdf"
) )
@bp.route("/<uuid>/attachments") @bp.route("/<uuid>/attachments")
@ -51,7 +54,7 @@ def add_attachment(uuid):
user = User(user_id=session.get("user_id")) user = User(user_id=session.get("user_id"))
if user.id != partition.user_id and user.access_level != 1: if user.id != partition.user_id and user.access_level != 1:
flash("Cette partition ne vous appartient pas") flash("Cette partition ne vous current_appartient pas")
return redirect(request.referrer) return redirect(request.referrer)
error = None # À mettre au propre error = None # À mettre au propre
@ -90,7 +93,11 @@ def add_attachment(uuid):
db.commit() db.commit()
file = request.files["file"] file = request.files["file"]
file.save(f"partitioncloud/attachments/{attachment_uuid}.{ext}") file.save(os.path.join(
current_app.instance_path,
"attachments",
f"{attachment_uuid}.{ext}"
))
break break
except db.IntegrityError: except db.IntegrityError:
@ -114,9 +121,11 @@ def get_attachment(uuid, filetype):
assert filetype == attachment.filetype assert filetype == attachment.filetype
return send_file( return send_file(os.path.join(
os.path.join("attachments", f"{uuid}.{attachment.filetype}"), current_app.instance_path,
download_name = f"{attachment.name}.{attachment.filetype}" "attachments",
f"{uuid}.{attachment.filetype}"
), download_name = f"{attachment.name}.{attachment.filetype}"
) )
@ -223,7 +232,7 @@ def delete(uuid):
if request.method == "GET": if request.method == "GET":
return render_template("partition/delete.html", partition=partition, user=user) return render_template("partition/delete.html", partition=partition, user=user)
partition.delete() partition.delete(current_app.instance_path)
flash("Partition supprimée.") flash("Partition supprimée.")
return redirect("/albums") return redirect("/albums")
@ -245,7 +254,13 @@ def partition_search(uuid):
abort(404) abort(404)
if request.args.get("redirect") == "true" and partition["url"] is not None: if request.args.get("redirect") == "true" and partition["url"] is not None:
return redirect(partition["url"]) return redirect(partition["url"])
return send_file(os.path.join("search-partitions", f"{uuid}.pdf"))
return send_file(os.path.join(
current_app.instance_path,
"search-partitions",
f"{uuid}.pdf"
)
)
@bp.route("/") @bp.route("/")

View File

@ -49,19 +49,19 @@ def local_search(query, partitions):
return selection return selection
def download_search_result(element): def download_search_result(element, instance_path):
uuid = element["uuid"] uuid = element["uuid"]
url = element["url"] url = element["url"]
try: try:
urllib.request.urlretrieve(url, f"partitioncloud/search-partitions/{uuid}.pdf") urllib.request.urlretrieve(url, f"{instance_path}/search-partitions/{uuid}.pdf")
except (urllib.error.HTTPError, urllib.error.URLError): except (urllib.error.HTTPError, urllib.error.URLError):
with open(f"partitioncloud/search-partitions/{uuid}.pdf", 'a', encoding="utf8") as _: with open(f"{instance_path}/search-partitions/{uuid}.pdf", 'a', encoding="utf8") as _:
pass # Create empty file pass # Create empty file
def online_search(query, num_queries): def online_search(query, num_queries, instance_path):
""" """
Renvoie les 3 résultats les plus pertinents depuis google Renvoie les 3 résultats les plus pertinents depuis google
""" """
@ -103,7 +103,12 @@ def online_search(query, num_queries):
except urllib.error.URLError: # Unable to access network except urllib.error.URLError: # Unable to access network
return [] return []
threads = [threading.Thread(target=download_search_result, args=(elem,)) for elem in partitions] threads = [
threading.Thread(
target=download_search_result,
args=(elem, instance_path)
) for elem in partitions
]
for thread in threads: for thread in threads:
thread.start() thread.start()
@ -114,7 +119,7 @@ def online_search(query, num_queries):
for element in partitions.copy(): for element in partitions.copy():
uuid = element["uuid"] uuid = element["uuid"]
url = element["url"] url = element["url"]
if os.stat(f"partitioncloud/search-partitions/{uuid}.pdf").st_size == 0: if os.stat(f"{instance_path}/search-partitions/{uuid}.pdf").st_size == 0:
print("An error occured", url) print("An error occured", url)
db.execute( db.execute(
""" """
@ -125,14 +130,14 @@ def online_search(query, num_queries):
) )
db.commit() db.commit()
os.remove(f"partitioncloud/search-partitions/{uuid}.pdf") os.remove(f"{instance_path}/search-partitions/{uuid}.pdf")
partitions.remove(element) partitions.remove(element)
return partitions return partitions
def flush_cache(): def flush_cache(instance_path):
""" """
Supprimer les résultats de recherche datant de plus de 15 minutes Supprimer les résultats de recherche datant de plus de 15 minutes
""" """
@ -146,7 +151,7 @@ def flush_cache():
for element in expired_cache: for element in expired_cache:
uuid = element["uuid"] uuid = element["uuid"]
try: try:
os.remove(f"partitioncloud/search-partitions/{uuid}.pdf") os.remove(f"{instance_path}/search-partitions/{uuid}.pdf")
except FileNotFoundError: except FileNotFoundError:
pass pass
try: try:

View File

@ -1,4 +1,5 @@
import os import os
import shutil
import sqlite3 import sqlite3
from hooks import utils from hooks import utils
from colorama import Fore, Style from colorama import Fore, Style
@ -142,3 +143,21 @@ def base_url_parameter_added():
def install_qrcode(): def install_qrcode():
os.system("pip install qrcode -qq") os.system("pip install qrcode -qq")
"""
v1.5.*
"""
def move_instance():
paths = [
"attachments",
"partitions",
"search-partitions"
]
for path in paths:
shutil.move(
os.path.join("partitioncloud", path),
os.path.join("instance", path)
)

View File

@ -33,6 +33,7 @@ hooks = [
], ],
), ),
("v1.4.1", [("Install qrcode", v1_hooks.install_qrcode)]), ("v1.4.1", [("Install qrcode", v1_hooks.install_qrcode)]),
("v1.5.0", [("Move to instance directory", v1_hooks.move_instance)])
] ]
@ -86,10 +87,10 @@ def backup_instance(version, verbose=True):
os.path.join(dest, "search-partitions"), os.path.join(dest, "search-partitions"),
), ),
] ]
for src, dst in paths: for src, dst in paths: # Only the first one exists after v1.5.0
if os.path.exists(src): if os.path.exists(src):
print_verbose(f"\tBacking up {src}") print_verbose(f"\tBacking up {src}")
shutil.copy_tree(src, dst) shutil.copytree(src, dst)
def print_hooks(hooks_list): def print_hooks(hooks_list):
@ -108,7 +109,7 @@ def apply_hooks(hooks_list):
def migrate(current, target, skip_backup=False, prog_name="scripts/migration.py"): def migrate(current, target, skip_backup=False, prog_name="scripts/migration.py"):
"""""" """Migrate from one version to another"""
print(f"Trying to migrate from {current} to {target}") print(f"Trying to migrate from {current} to {target}")
assert is_newer(target, current) assert is_newer(target, current)
@ -168,13 +169,13 @@ def restore(version):
os.path.join(dest, "search-partitions"), os.path.join(dest, "search-partitions"),
), ),
] ]
for src, dst in paths: for src, dst in paths: # Only the first one exists after v1.5.0
if os.path.exists(src): if os.path.exists(src):
shutil.rmtree(src) shutil.rmtree(src)
if os.path.exists(dst): if os.path.exists(dst):
print(f"\tRestoring {src}") print(f"\tRestoring {src}")
shutil.copy_tree(dst, src) shutil.copytree(dst, src)
else: else:
print( print(
f"\t{Fore.RED}No available backup for {src}, \ f"\t{Fore.RED}No available backup for {src}, \