mirror of
https://github.com/Part-DB/Part-DB-server.git
synced 2025-06-21 09:35:49 +02:00
Compare commits
No commits in common. "master" and "v1.7.3" have entirely different histories.
793 changed files with 19634 additions and 150776 deletions
|
@ -1,55 +0,0 @@
|
||||||
{
|
|
||||||
{$CADDY_GLOBAL_OPTIONS}
|
|
||||||
|
|
||||||
frankenphp {
|
|
||||||
{$FRANKENPHP_CONFIG}
|
|
||||||
}
|
|
||||||
|
|
||||||
# https://caddyserver.com/docs/caddyfile/directives#sorting-algorithm
|
|
||||||
order mercure after encode
|
|
||||||
order vulcain after reverse_proxy
|
|
||||||
order php_server before file_server
|
|
||||||
}
|
|
||||||
|
|
||||||
{$CADDY_EXTRA_CONFIG}
|
|
||||||
|
|
||||||
{$SERVER_NAME:localhost} {
|
|
||||||
log {
|
|
||||||
# Redact the authorization query parameter that can be set by Mercure
|
|
||||||
format filter {
|
|
||||||
wrap console
|
|
||||||
fields {
|
|
||||||
uri query {
|
|
||||||
replace authorization REDACTED
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
root * /app/public
|
|
||||||
encode zstd br gzip
|
|
||||||
|
|
||||||
mercure {
|
|
||||||
# Transport to use (default to Bolt)
|
|
||||||
transport_url {$MERCURE_TRANSPORT_URL:bolt:///data/mercure.db}
|
|
||||||
# Publisher JWT key
|
|
||||||
publisher_jwt {env.MERCURE_PUBLISHER_JWT_KEY} {env.MERCURE_PUBLISHER_JWT_ALG}
|
|
||||||
# Subscriber JWT key
|
|
||||||
subscriber_jwt {env.MERCURE_SUBSCRIBER_JWT_KEY} {env.MERCURE_SUBSCRIBER_JWT_ALG}
|
|
||||||
# Allow anonymous subscribers (double-check that it's what you want)
|
|
||||||
anonymous
|
|
||||||
# Enable the subscription API (double-check that it's what you want)
|
|
||||||
subscriptions
|
|
||||||
# Extra directives
|
|
||||||
{$MERCURE_EXTRA_DIRECTIVES}
|
|
||||||
}
|
|
||||||
|
|
||||||
vulcain
|
|
||||||
|
|
||||||
{$CADDY_SERVER_EXTRA_DIRECTIVES}
|
|
||||||
|
|
||||||
# Disable Topics tracking if not enabled explicitly: https://github.com/jkarlin/topics
|
|
||||||
header ?Permissions-Policy "browsing-topics=()"
|
|
||||||
|
|
||||||
php_server
|
|
||||||
}
|
|
|
@ -1,5 +0,0 @@
|
||||||
; See https://docs.docker.com/desktop/networking/#i-want-to-connect-from-a-container-to-a-service-on-the-host
|
|
||||||
; See https://github.com/docker/for-linux/issues/264
|
|
||||||
; The `client_host` below may optionally be replaced with `discover_client_host=yes`
|
|
||||||
; Add `start_with_request=yes` to start debug session on each request
|
|
||||||
xdebug.client_host = host.docker.internal
|
|
|
@ -1,18 +0,0 @@
|
||||||
expose_php = 0
|
|
||||||
date.timezone = UTC
|
|
||||||
apc.enable_cli = 1
|
|
||||||
session.use_strict_mode = 1
|
|
||||||
zend.detect_unicode = 0
|
|
||||||
|
|
||||||
; https://symfony.com/doc/current/performance.html
|
|
||||||
realpath_cache_size = 4096K
|
|
||||||
realpath_cache_ttl = 600
|
|
||||||
opcache.interned_strings_buffer = 16
|
|
||||||
opcache.max_accelerated_files = 20000
|
|
||||||
opcache.memory_consumption = 256
|
|
||||||
opcache.enable_file_override = 1
|
|
||||||
|
|
||||||
memory_limit = 256M
|
|
||||||
|
|
||||||
upload_max_filesize=256M
|
|
||||||
post_max_size=300M
|
|
|
@ -1,2 +0,0 @@
|
||||||
opcache.preload_user = root
|
|
||||||
opcache.preload = /app/config/preload.php
|
|
|
@ -1,60 +0,0 @@
|
||||||
#!/bin/sh
|
|
||||||
set -e
|
|
||||||
|
|
||||||
if [ "$1" = 'frankenphp' ] || [ "$1" = 'php' ] || [ "$1" = 'bin/console' ]; then
|
|
||||||
# Install the project the first time PHP is started
|
|
||||||
# After the installation, the following block can be deleted
|
|
||||||
if [ ! -f composer.json ]; then
|
|
||||||
rm -Rf tmp/
|
|
||||||
composer create-project "symfony/skeleton $SYMFONY_VERSION" tmp --stability="$STABILITY" --prefer-dist --no-progress --no-interaction --no-install
|
|
||||||
|
|
||||||
cd tmp
|
|
||||||
cp -Rp . ..
|
|
||||||
cd -
|
|
||||||
rm -Rf tmp/
|
|
||||||
|
|
||||||
composer require "php:>=$PHP_VERSION" runtime/frankenphp-symfony
|
|
||||||
composer config --json extra.symfony.docker 'true'
|
|
||||||
|
|
||||||
if grep -q ^DATABASE_URL= .env; then
|
|
||||||
echo "To finish the installation please press Ctrl+C to stop Docker Compose and run: docker compose up --build -d --wait"
|
|
||||||
sleep infinity
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ -z "$(ls -A 'vendor/' 2>/dev/null)" ]; then
|
|
||||||
composer install --prefer-dist --no-progress --no-interaction
|
|
||||||
fi
|
|
||||||
|
|
||||||
if grep -q ^DATABASE_URL= .env; then
|
|
||||||
echo "Waiting for database to be ready..."
|
|
||||||
ATTEMPTS_LEFT_TO_REACH_DATABASE=60
|
|
||||||
until [ $ATTEMPTS_LEFT_TO_REACH_DATABASE -eq 0 ] || DATABASE_ERROR=$(php bin/console dbal:run-sql -q "SELECT 1" 2>&1); do
|
|
||||||
if [ $? -eq 255 ]; then
|
|
||||||
# If the Doctrine command exits with 255, an unrecoverable error occurred
|
|
||||||
ATTEMPTS_LEFT_TO_REACH_DATABASE=0
|
|
||||||
break
|
|
||||||
fi
|
|
||||||
sleep 1
|
|
||||||
ATTEMPTS_LEFT_TO_REACH_DATABASE=$((ATTEMPTS_LEFT_TO_REACH_DATABASE - 1))
|
|
||||||
echo "Still waiting for database to be ready... Or maybe the database is not reachable. $ATTEMPTS_LEFT_TO_REACH_DATABASE attempts left."
|
|
||||||
done
|
|
||||||
|
|
||||||
if [ $ATTEMPTS_LEFT_TO_REACH_DATABASE -eq 0 ]; then
|
|
||||||
echo "The database is not up or not reachable:"
|
|
||||||
echo "$DATABASE_ERROR"
|
|
||||||
exit 1
|
|
||||||
else
|
|
||||||
echo "The database is now ready and reachable"
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ "$( find ./migrations -iname '*.php' -print -quit )" ]; then
|
|
||||||
php bin/console doctrine:migrations:migrate --no-interaction
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
setfacl -R -m u:www-data:rwX -m u:"$(whoami)":rwX var
|
|
||||||
setfacl -dR -m u:www-data:rwX -m u:"$(whoami)":rwX var
|
|
||||||
fi
|
|
||||||
|
|
||||||
exec docker-php-entrypoint "$@"
|
|
|
@ -1,4 +0,0 @@
|
||||||
worker {
|
|
||||||
file ./public/index.php
|
|
||||||
env APP_RUNTIME Runtime\FrankenPhpSymfony\Runtime
|
|
||||||
}
|
|
|
@ -39,50 +39,8 @@ if [ -d /var/www/html/var/db ]; then
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Start PHP-FPM (the PHP_VERSION is replaced by the configured version in the Dockerfile)
|
# Start PHP-FPM
|
||||||
service phpPHP_VERSION-fpm start
|
service php8.1-fpm start
|
||||||
|
|
||||||
|
|
||||||
# Run migrations if automigration is enabled via env variable DB_AUTOMIGRATE
|
|
||||||
if [ "$DB_AUTOMIGRATE" = "true" ]; then
|
|
||||||
echo "Waiting for database to be ready..."
|
|
||||||
ATTEMPTS_LEFT_TO_REACH_DATABASE=60
|
|
||||||
until [ $ATTEMPTS_LEFT_TO_REACH_DATABASE -eq 0 ] || DATABASE_ERROR=$(sudo -E -u www-data php bin/console dbal:run-sql -q "SELECT 1" 2>&1); do
|
|
||||||
if [ $? -eq 255 ]; then
|
|
||||||
# If the Doctrine command exits with 255, an unrecoverable error occurred
|
|
||||||
ATTEMPTS_LEFT_TO_REACH_DATABASE=0
|
|
||||||
break
|
|
||||||
fi
|
|
||||||
sleep 1
|
|
||||||
ATTEMPTS_LEFT_TO_REACH_DATABASE=$((ATTEMPTS_LEFT_TO_REACH_DATABASE - 1))
|
|
||||||
echo "Still waiting for database to be ready... Or maybe the database is not reachable. $ATTEMPTS_LEFT_TO_REACH_DATABASE attempts left."
|
|
||||||
done
|
|
||||||
|
|
||||||
if [ $ATTEMPTS_LEFT_TO_REACH_DATABASE -eq 0 ]; then
|
|
||||||
echo "The database is not up or not reachable:"
|
|
||||||
echo "$DATABASE_ERROR"
|
|
||||||
exit 1
|
|
||||||
else
|
|
||||||
echo "The database is now ready and reachable"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Check if there are any available migrations to do, by executing doctrine:migrations:up-to-date
|
|
||||||
# and checking if the exit code is 0 (up to date) or 1 (not up to date)
|
|
||||||
if sudo -E -u www-data php bin/console doctrine:migrations:up-to-date --no-interaction; then
|
|
||||||
echo "Database is up to date, no migrations necessary."
|
|
||||||
else
|
|
||||||
echo "Migrations available..."
|
|
||||||
echo "Do backup of database..."
|
|
||||||
|
|
||||||
sudo -E -u www-data mkdir -p /var/www/html/uploads/.automigration-backup/
|
|
||||||
# Backup the database
|
|
||||||
sudo -E -u www-data php bin/console partdb:backup -n --database /var/www/html/uploads/.automigration-backup/backup-$(date +%Y-%m-%d_%H-%M-%S).zip
|
|
||||||
|
|
||||||
# Check if there are any migration files
|
|
||||||
sudo -E -u www-data php bin/console doctrine:migrations:migrate --no-interaction
|
|
||||||
fi
|
|
||||||
|
|
||||||
fi
|
|
||||||
|
|
||||||
# first arg is `-f` or `--some-option` (taken from https://github.com/docker-library/php/blob/master/8.2/bullseye/apache/docker-php-entrypoint)
|
# first arg is `-f` or `--some-option` (taken from https://github.com/docker-library/php/blob/master/8.2/bullseye/apache/docker-php-entrypoint)
|
||||||
if [ "${1#-}" != "$1" ]; then
|
if [ "${1#-}" != "$1" ]; then
|
||||||
|
|
|
@ -25,28 +25,22 @@
|
||||||
CustomLog ${APACHE_LOG_DIR}/access.log combined
|
CustomLog ${APACHE_LOG_DIR}/access.log combined
|
||||||
|
|
||||||
# Pass the configuration from the docker env to the PHP environment (here you should list all .env options)
|
# Pass the configuration from the docker env to the PHP environment (here you should list all .env options)
|
||||||
PassEnv APP_ENV APP_DEBUG APP_SECRET REDIRECT_TO_HTTPS DISABLE_YEAR2038_BUG_CHECK
|
PassEnv APP_ENV APP_DEBUG APP_SECRET
|
||||||
PassEnv TRUSTED_PROXIES TRUSTED_HOSTS LOCK_DSN
|
PassEnv TRUSTED_PROXIES TRUSTED_HOSTS LOCK_DSN
|
||||||
PassEnv DATABASE_URL ENFORCE_CHANGE_COMMENTS_FOR DATABASE_MYSQL_USE_SSL_CA DATABASE_MYSQL_SSL_VERIFY_CERT
|
PassEnv DATABASE_URL ENFORCE_CHANGE_COMMENTS_FOR
|
||||||
PassEnv DEFAULT_LANG DEFAULT_TIMEZONE BASE_CURRENCY INSTANCE_NAME ALLOW_ATTACHMENT_DOWNLOADS USE_GRAVATAR MAX_ATTACHMENT_FILE_SIZE DEFAULT_URI CHECK_FOR_UPDATES ATTACHMENT_DOWNLOAD_BY_DEFAULT
|
PassEnv DEFAULT_LANG DEFAULT_TIMEZONE BASE_CURRENCY INSTANCE_NAME ALLOW_ATTACHMENT_DOWNLOADS USE_GRAVATAR MAX_ATTACHMENT_FILE_SIZE DEFAULT_URI CHECK_FOR_UPDATES
|
||||||
PassEnv MAILER_DSN ALLOW_EMAIL_PW_RESET EMAIL_SENDER_EMAIL EMAIL_SENDER_NAME
|
PassEnv MAILER_DSN ALLOW_EMAIL_PW_RESET EMAIL_SENDER_EMAIL EMAIL_SENDER_NAME
|
||||||
PassEnv HISTORY_SAVE_CHANGED_FIELDS HISTORY_SAVE_CHANGED_DATA HISTORY_SAVE_REMOVED_DATA HISTORY_SAVE_NEW_DATA
|
PassEnv HISTORY_SAVE_CHANGED_FIELDS HISTORY_SAVE_CHANGED_DATA HISTORY_SAVE_REMOVED_DATA HISTORY_SAVE_NEW_DATA
|
||||||
PassEnv ERROR_PAGE_ADMIN_EMAIL ERROR_PAGE_SHOW_HELP
|
PassEnv ERROR_PAGE_ADMIN_EMAIL ERROR_PAGE_SHOW_HELP
|
||||||
PassEnv DEMO_MODE NO_URL_REWRITE_AVAILABLE FIXER_API_KEY BANNER
|
PassEnv DEMO_MODE NO_URL_REWRITE_AVAILABLE FIXER_API_KEY BANNER
|
||||||
# In old version the SAML sp private key env, was wrongly named SAMLP_SP_PRIVATE_KEY, keep it for backward compatibility
|
# In old version the SAML sp private key env, was wrongly named SAMLP_SP_PRIVATE_KEY, keep it for backward compatibility
|
||||||
PassEnv SAML_ENABLED SAML_BEHIND_PROXY SAML_ROLE_MAPPING SAML_UPDATE_GROUP_ON_LOGIN SAML_IDP_ENTITY_ID SAML_IDP_SINGLE_SIGN_ON_SERVICE SAML_IDP_SINGLE_LOGOUT_SERVICE SAML_IDP_X509_CERT SAML_SP_ENTITY_ID SAML_SP_X509_CERT SAML_SP_PRIVATE_KEY SAMLP_SP_PRIVATE_KEY
|
PassEnv SAML_ENABLED SAML_ROLE_MAPPING SAML_UPDATE_GROUP_ON_LOGIN SAML_IDP_ENTITY_ID SAML_IDP_SINGLE_SIGN_ON_SERVICE SAML_IDP_SINGLE_LOGOUT_SERVICE SAML_IDP_X509_CERT SAML_SP_ENTITY_ID SAML_SP_X509_CERT SAML_SP_PRIVATE_KEY SAMLP_SP_PRIVATE_KEY
|
||||||
PassEnv TABLE_DEFAULT_PAGE_SIZE TABLE_PARTS_DEFAULT_COLUMNS
|
PassEnv TABLE_DEFAULT_PAGE_SIZE
|
||||||
|
|
||||||
PassEnv PROVIDER_DIGIKEY_CLIENT_ID PROVIDER_DIGIKEY_SECRET PROVIDER_DIGIKEY_CURRENCY PROVIDER_DIGIKEY_LANGUAGE PROVIDER_DIGIKEY_COUNTRY
|
PassEnv PROVIDER_DIGIKEY_CLIENT_ID PROVIDER_DIGIKEY_SECRET PROVIDER_DIGIKEY_CURRENCY PROVIDER_DIGIKEY_LANGUAGE PROVIDER_DIGIKEY_COUNTRY
|
||||||
PassEnv PROVIDER_ELEMENT14_KEY PROVIDER_ELEMENT14_STORE_ID
|
PassEnv PROVIDER_ELEMENT14_KEY PROVIDER_ELEMENT14_STORE_ID
|
||||||
PassEnv PROVIDER_TME_KEY PROVIDER_TME_SECRET PROVIDER_TME_CURRENCY PROVIDER_TME_LANGUAGE PROVIDER_TME_COUNTRY PROVIDER_TME_GET_GROSS_PRICES
|
PassEnv PROVIDER_TME_KEY PROVIDER_TME_SECRET PROVIDER_TME_CURRENCY PROVIDER_TME_LANGUAGE PROVIDER_TME_COUNTRY PROVIDER_TME_GET_GROSS_PRICES
|
||||||
PassEnv PROVIDER_OCTOPART_CLIENT_ID PROVIDER_OCTOPART_SECRET PROVIDER_OCTOPART_CURRENCY PROVIDER_OCTOPART_COUNTRY PROVIDER_OCTOPART_SEARCH_LIMIT PROVIDER_OCTOPART_ONLY_AUTHORIZED_SELLERS
|
PassEnv PROVIDER_OCTOPART_CLIENT_ID PROVIDER_OCTOPART_SECRET PROVIDER_OCTOPART_CURRENCY PROVIDER_OCTOPART_COUNTRY PROVIDER_OCTOPART_SEARCH_LIMIT PROVIDER_OCTOPART_ONLY_AUTHORIZED_SELLERS
|
||||||
PassEnv PROVIDER_MOUSER_KEY PROVIDER_MOUSER_SEARCH_OPTION PROVIDER_MOUSER_SEARCH_LIMIT PROVIDER_MOUSER_SEARCH_WITH_SIGNUP_LANGUAGE
|
|
||||||
PassEnv PROVIDER_LCSC_ENABLED PROVIDER_LCSC_CURRENCY
|
|
||||||
PassEnv PROVIDER_OEMSECRETS_KEY PROVIDER_OEMSECRETS_COUNTRY_CODE PROVIDER_OEMSECRETS_CURRENCY PROVIDER_OEMSECRETS_ZERO_PRICE PROVIDER_OEMSECRETS_SET_PARAM PROVIDER_OEMSECRETS_SORT_CRITERIA
|
|
||||||
PassEnv PROVIDER_REICHELT_ENABLED PROVIDER_REICHELT_CURRENCY PROVIDER_REICHELT_COUNTRY PROVIDER_REICHELT_LANGUAGE PROVIDER_REICHELT_INCLUDE_VAT
|
|
||||||
PassEnv PROVIDER_POLLIN_ENABLED
|
|
||||||
PassEnv EDA_KICAD_CATEGORY_DEPTH
|
|
||||||
|
|
||||||
# For most configuration files from conf-available/, which are
|
# For most configuration files from conf-available/, which are
|
||||||
# enabled or disabled at a global level, it is possible to
|
# enabled or disabled at a global level, it is possible to
|
||||||
|
|
|
@ -5,8 +5,6 @@ tests/
|
||||||
docs/
|
docs/
|
||||||
.git
|
.git
|
||||||
|
|
||||||
/public/media/*
|
|
||||||
|
|
||||||
###> symfony/framework-bundle ###
|
###> symfony/framework-bundle ###
|
||||||
/.env.local
|
/.env.local
|
||||||
/.env.local.php
|
/.env.local.php
|
||||||
|
@ -44,39 +42,3 @@ yarn-error.log
|
||||||
/phpunit.xml
|
/phpunit.xml
|
||||||
.phpunit.result.cache
|
.phpunit.result.cache
|
||||||
###< phpunit/phpunit ###
|
###< phpunit/phpunit ###
|
||||||
|
|
||||||
|
|
||||||
### From frankenphp
|
|
||||||
|
|
||||||
**/*.log
|
|
||||||
**/*.php~
|
|
||||||
**/*.dist.php
|
|
||||||
**/*.dist
|
|
||||||
**/*.cache
|
|
||||||
**/._*
|
|
||||||
**/.dockerignore
|
|
||||||
**/.DS_Store
|
|
||||||
**/.git/
|
|
||||||
**/.gitattributes
|
|
||||||
**/.gitignore
|
|
||||||
**/.gitmodules
|
|
||||||
**/compose.*.yaml
|
|
||||||
**/compose.*.yml
|
|
||||||
**/compose.yaml
|
|
||||||
**/compose.yml
|
|
||||||
**/docker-compose.*.yaml
|
|
||||||
**/docker-compose.*.yml
|
|
||||||
**/docker-compose.yaml
|
|
||||||
**/docker-compose.yml
|
|
||||||
**/Dockerfile
|
|
||||||
**/Thumbs.db
|
|
||||||
.github/
|
|
||||||
public/bundles/
|
|
||||||
var/
|
|
||||||
vendor/
|
|
||||||
.editorconfig
|
|
||||||
.env.*.local
|
|
||||||
.env.local
|
|
||||||
.env.local.php
|
|
||||||
.env.test
|
|
||||||
|
|
||||||
|
|
122
.env
122
.env
|
@ -14,19 +14,6 @@ DATABASE_URL="sqlite:///%kernel.project_dir%/var/app.db"
|
||||||
# Uncomment this line (and comment the line above to use a MySQL database
|
# Uncomment this line (and comment the line above to use a MySQL database
|
||||||
#DATABASE_URL=mysql://root:@127.0.0.1:3306/part-db?serverVersion=5.7
|
#DATABASE_URL=mysql://root:@127.0.0.1:3306/part-db?serverVersion=5.7
|
||||||
|
|
||||||
# Set this value to 1, if you want to use SSL to connect to the MySQL server. It will be tried to use the CA certificate
|
|
||||||
# otherwise a CA bundle shipped with PHP will be used.
|
|
||||||
# Leave it at 0, if you do not want to use SSL or if your server does not support it
|
|
||||||
DATABASE_MYSQL_USE_SSL_CA=0
|
|
||||||
|
|
||||||
# Set this value to 0, if you don't want to verify the CA certificate of the MySQL server
|
|
||||||
# Only do this, if you know what you are doing!
|
|
||||||
DATABASE_MYSQL_SSL_VERIFY_CERT=1
|
|
||||||
|
|
||||||
# Emulate natural sorting of strings even on databases that do not support it (like SQLite, MySQL or MariaDB < 10.7)
|
|
||||||
# This can be slow on big databases and might have some problems and quirks, so use it with caution
|
|
||||||
DATABASE_EMULATE_NATURAL_SORT=0
|
|
||||||
|
|
||||||
###################################################################################
|
###################################################################################
|
||||||
# General settings
|
# General settings
|
||||||
###################################################################################
|
###################################################################################
|
||||||
|
@ -42,15 +29,13 @@ INSTANCE_NAME="Part-DB"
|
||||||
# Allow users to download attachments to the server by providing an URL
|
# Allow users to download attachments to the server by providing an URL
|
||||||
# This could be a potential security issue, as the user can retrieve any file the server has access to (via internet)
|
# This could be a potential security issue, as the user can retrieve any file the server has access to (via internet)
|
||||||
ALLOW_ATTACHMENT_DOWNLOADS=0
|
ALLOW_ATTACHMENT_DOWNLOADS=0
|
||||||
# Set this to 1, if the "download external files" checkbox should be checked by default for new attachments
|
|
||||||
ATTACHMENT_DOWNLOAD_BY_DEFAULT=0
|
|
||||||
# Use gravatars for user avatars, when user has no own avatar defined
|
# Use gravatars for user avatars, when user has no own avatar defined
|
||||||
USE_GRAVATAR=0
|
USE_GRAVATAR=0
|
||||||
# The maximum allowed size for attachment files in bytes (you can use M for megabytes and G for gigabytes)
|
# The maximum allowed size for attachment files in bytes (you can use M for megabytes and G for gigabytes)
|
||||||
# Please note that the php.ini setting upload_max_filesize also limits the maximum size of uploaded files
|
# Please note that the php.ini setting upload_max_filesize also limits the maximum size of uploaded files
|
||||||
MAX_ATTACHMENT_FILE_SIZE="100M"
|
MAX_ATTACHMENT_FILE_SIZE="100M"
|
||||||
|
|
||||||
# The public reachable URL of this Part-DB installation. This is used for generating links in SAML and email templates
|
# The public reachable URL of this Part-DB installation. This is used for generating links to the website in emails and so on
|
||||||
# This must end with a slash!
|
# This must end with a slash!
|
||||||
DEFAULT_URI="https://partdb.changeme.invalid/"
|
DEFAULT_URI="https://partdb.changeme.invalid/"
|
||||||
|
|
||||||
|
@ -108,9 +93,6 @@ ERROR_PAGE_SHOW_HELP=1
|
||||||
|
|
||||||
# The default page size for the part table (set to -1 to show all parts on one page)
|
# The default page size for the part table (set to -1 to show all parts on one page)
|
||||||
TABLE_DEFAULT_PAGE_SIZE=50
|
TABLE_DEFAULT_PAGE_SIZE=50
|
||||||
# Configure which columns will be visible by default in the parts table (and in which order).
|
|
||||||
# This is a comma separated list of column names. See documentation for available values.
|
|
||||||
TABLE_PARTS_DEFAULT_COLUMNS=name,description,category,footprint,manufacturer,storage_location,amount
|
|
||||||
|
|
||||||
##################################################################################
|
##################################################################################
|
||||||
# Info provider settings
|
# Info provider settings
|
||||||
|
@ -143,8 +125,7 @@ PROVIDER_TME_CURRENCY=EUR
|
||||||
PROVIDER_TME_LANGUAGE=en
|
PROVIDER_TME_LANGUAGE=en
|
||||||
# The country to get results for
|
# The country to get results for
|
||||||
PROVIDER_TME_COUNTRY=DE
|
PROVIDER_TME_COUNTRY=DE
|
||||||
# [DEPRECATED] Set this to 1 to get gross prices (including VAT) instead of net prices
|
# Set this to 1 to get gross prices (including VAT) instead of net prices
|
||||||
# With private API keys, this option cannot be used anymore is ignored by Part-DB. The VAT inclusion depends on your TME account settings.
|
|
||||||
PROVIDER_TME_GET_GROSS_PRICES=1
|
PROVIDER_TME_GET_GROSS_PRICES=1
|
||||||
|
|
||||||
# Octopart / Nexar Provider:
|
# Octopart / Nexar Provider:
|
||||||
|
@ -161,101 +142,13 @@ PROVIDER_OCTOPART_SEARCH_LIMIT=10
|
||||||
# Set to false to include non authorized offers in the results
|
# Set to false to include non authorized offers in the results
|
||||||
PROVIDER_OCTOPART_ONLY_AUTHORIZED_SELLERS=1
|
PROVIDER_OCTOPART_ONLY_AUTHORIZED_SELLERS=1
|
||||||
|
|
||||||
# Mouser Provider API V2:
|
|
||||||
# You can get your API key from https://www.mouser.it/api-hub/
|
|
||||||
PROVIDER_MOUSER_KEY=
|
|
||||||
# Filter search results by RoHS compliance and stock availability:
|
|
||||||
# Available options: None | Rohs | InStock | RohsAndInStock
|
|
||||||
PROVIDER_MOUSER_SEARCH_OPTION='None'
|
|
||||||
# The number of results to get from Mouser while searching (please note that this value is max 50)
|
|
||||||
PROVIDER_MOUSER_SEARCH_LIMIT=50
|
|
||||||
# It is recommended to leave this set to 'true'. The option is not really good doumented by Mouser:
|
|
||||||
# Used when searching for keywords in the language specified when you signed up for Search API.
|
|
||||||
PROVIDER_MOUSER_SEARCH_WITH_SIGNUP_LANGUAGE='true'
|
|
||||||
|
|
||||||
# LCSC Provider:
|
|
||||||
# LCSC does not provide an offical API, so this used the API LCSC uses to render their webshop.
|
|
||||||
# LCSC did not intended the use of this API and it could break any time, so use it at your own risk.
|
|
||||||
|
|
||||||
# We dont require an API key for LCSC, just set this to 1 to enable LCSC support
|
|
||||||
PROVIDER_LCSC_ENABLED=0
|
|
||||||
# The currency to get prices in (e.g. EUR, USD, etc.)
|
|
||||||
PROVIDER_LCSC_CURRENCY=EUR
|
|
||||||
|
|
||||||
# Oemsecrets Provider API 3.0.1:
|
|
||||||
# You can get your API key from https://www.oemsecrets.com/api
|
|
||||||
PROVIDER_OEMSECRETS_KEY=
|
|
||||||
# The country you want the output for
|
|
||||||
PROVIDER_OEMSECRETS_COUNTRY_CODE=DE
|
|
||||||
# Available country code are:
|
|
||||||
# AD, AE, AQ, AR, AT, AU, BE, BO, BR, BV, BY, CA, CH, CL, CN, CO, CZ, DE, DK, EC, EE, EH,
|
|
||||||
# ES, FI, FK, FO, FR, GB, GE, GF, GG, GI, GL, GR, GS, GY, HK, HM, HR, HU, IE, IM, IN, IS,
|
|
||||||
# IT, JM, JP, KP, KR, KZ, LI, LK, LT, LU, MC, MD, ME, MK, MT, NL, NO, NZ, PE, PH, PL, PT,
|
|
||||||
# PY, RO, RS, RU, SB, SD, SE, SG, SI, SJ, SK, SM, SO, SR, SY, SZ, TC, TF, TG, TH, TJ, TK,
|
|
||||||
# TM, TN, TO, TR, TT, TV, TW, TZ, UA, UG, UM, US, UY, UZ, VA, VE, VG, VI, VN, VU, WF, YE,
|
|
||||||
# ZA, ZM, ZW
|
|
||||||
#
|
|
||||||
# The currency you want the prices to be displayed in
|
|
||||||
PROVIDER_OEMSECRETS_CURRENCY=EUR
|
|
||||||
# Available currency are:AUD, CAD, CHF, CNY, DKK, EUR, GBP, HKD, ILS, INR, JPY, KRW, NOK,
|
|
||||||
# NZD, RUB, SEK, SGD, TWD, USD
|
|
||||||
#
|
|
||||||
# If PROVIDER_OEMSECRETS_ZERO_PRICE is set to 0, distributors with zero prices
|
|
||||||
# will be discarded from the creation of a new part (set to 1 otherwise)
|
|
||||||
PROVIDER_OEMSECRETS_ZERO_PRICE=0
|
|
||||||
#
|
|
||||||
# When PROVIDER_OEMSECRETS_SET_PARAM is set to 1 the parameters for the part are generated
|
|
||||||
# from the description transforming unstructured descriptions into structured parameters;
|
|
||||||
# each parameter in description should have the form: "...;name1:value1;name2:value2"
|
|
||||||
PROVIDER_OEMSECRETS_SET_PARAM=1
|
|
||||||
#
|
|
||||||
# This environment variable determines the sorting criteria for product results.
|
|
||||||
# The sorting process first arranges items based on the provided keyword.
|
|
||||||
# Then, if set to 'C', it further sorts by completeness (prioritizing items with the most
|
|
||||||
# detailed information). If set to 'M', it further sorts by manufacturer name.
|
|
||||||
#If unset or set to any other value, no sorting is performed.
|
|
||||||
PROVIDER_OEMSECRETS_SORT_CRITERIA=C
|
|
||||||
|
|
||||||
|
|
||||||
# Reichelt provider:
|
|
||||||
# Reichelt.com offers no official API, so this info provider webscrapes the website to extract info
|
|
||||||
# It could break at any time, use it at your own risk
|
|
||||||
# We dont require an API key for Reichelt, just set this to 1 to enable Reichelt support
|
|
||||||
PROVIDER_REICHELT_ENABLED=0
|
|
||||||
# The country to get prices for
|
|
||||||
PROVIDER_REICHELT_COUNTRY=DE
|
|
||||||
# The language to get results in (en, de, fr, nl, pl, it, es)
|
|
||||||
PROVIDER_REICHELT_LANGUAGE=en
|
|
||||||
# Include VAT in prices (set to 1 to include VAT, 0 to exclude VAT)
|
|
||||||
PROVIDER_REICHELT_INCLUDE_VAT=1
|
|
||||||
# The currency to get prices in (only for countries with countries other than EUR)
|
|
||||||
PROVIDER_REICHELT_CURRENCY=EUR
|
|
||||||
|
|
||||||
# Pollin provider:
|
|
||||||
# Pollin.de offers no official API, so this info provider webscrapes the website to extract info
|
|
||||||
# It could break at any time, use it at your own risk
|
|
||||||
# We dont require an API key for Pollin, just set this to 1 to enable Pollin support
|
|
||||||
PROVIDER_POLLIN_ENABLED=0
|
|
||||||
|
|
||||||
##################################################################################
|
|
||||||
# EDA integration related settings
|
|
||||||
##################################################################################
|
|
||||||
|
|
||||||
# This value determines the depth of the category tree, that is visible inside KiCad
|
|
||||||
# 0 means that only the top level categories are visible. Set to a value > 0 to show more levels.
|
|
||||||
# Set to -1, to show all parts of Part-DB inside a single category in KiCad
|
|
||||||
EDA_KICAD_CATEGORY_DEPTH=0
|
|
||||||
|
|
||||||
###################################################################################
|
###################################################################################
|
||||||
# SAML Single sign on-settings
|
# SAML Single sign on-settings
|
||||||
###################################################################################
|
###################################################################################
|
||||||
# Set this to 1 to enable SAML single sign on
|
# Set this to 1 to enable SAML single sign on
|
||||||
# Be also sure to set the correct values for DEFAULT_URI
|
|
||||||
SAML_ENABLED=0
|
SAML_ENABLED=0
|
||||||
|
|
||||||
# Set to 1, if your Part-DB installation is behind a reverse proxy and you want to use SAML
|
|
||||||
SAML_BEHIND_PROXY=0
|
|
||||||
|
|
||||||
# A JSON encoded array of role mappings in the form { "saml_role": PARTDB_GROUP_ID, "*": PARTDB_GROUP_ID }
|
# A JSON encoded array of role mappings in the form { "saml_role": PARTDB_GROUP_ID, "*": PARTDB_GROUP_ID }
|
||||||
# The first match is used, so the order is important! Put the group mapping with the most privileges first.
|
# The first match is used, so the order is important! Put the group mapping with the most privileges first.
|
||||||
# Please not to only use single quotes to enclose the JSON string
|
# Please not to only use single quotes to enclose the JSON string
|
||||||
|
@ -295,9 +188,6 @@ DEMO_MODE=0
|
||||||
# In that case all URL contains the index.php front controller in URL
|
# In that case all URL contains the index.php front controller in URL
|
||||||
NO_URL_REWRITE_AVAILABLE=0
|
NO_URL_REWRITE_AVAILABLE=0
|
||||||
|
|
||||||
# Set to 1, if Part-DB should redirect all HTTP requests to HTTPS. You dont need to configure this, if your webserver already does this.
|
|
||||||
REDIRECT_TO_HTTPS=0
|
|
||||||
|
|
||||||
# If you want to use fixer.io for currency conversion, you have to set this to your API key
|
# If you want to use fixer.io for currency conversion, you have to set this to your API key
|
||||||
FIXER_API_KEY=CHANGEME
|
FIXER_API_KEY=CHANGEME
|
||||||
|
|
||||||
|
@ -308,11 +198,9 @@ BANNER=""
|
||||||
APP_ENV=prod
|
APP_ENV=prod
|
||||||
APP_SECRET=a03498528f5a5fc089273ec9ae5b2849
|
APP_SECRET=a03498528f5a5fc089273ec9ae5b2849
|
||||||
|
|
||||||
# Set this to zero, if you want to disable the year 2038 bug check on 32-bit systems (it will cause errors with current 32-bit PHP versions)
|
|
||||||
DISABLE_YEAR2038_BUG_CHECK=0
|
|
||||||
|
|
||||||
# Set the trusted IPs here, when using an reverse proxy
|
# Set the trusted IPs here, when using an reverse proxy
|
||||||
#TRUSTED_PROXIES=127.0.0.0/8,::1,10.0.0.0/8,172.16.0.0/12,192.168.0.0/16
|
#TRUSTED_PROXIES=127.0.0.0/8,10.0.0.0/8,172.16.0.0/12,192.168.0.0/16
|
||||||
#TRUSTED_HOSTS='^(localhost|example\.com)$'
|
#TRUSTED_HOSTS='^(localhost|example\.com)$'
|
||||||
|
|
||||||
|
|
||||||
|
@ -321,7 +209,3 @@ DISABLE_YEAR2038_BUG_CHECK=0
|
||||||
# postgresql+advisory://db_user:db_password@localhost/db_name
|
# postgresql+advisory://db_user:db_password@localhost/db_name
|
||||||
LOCK_DSN=flock
|
LOCK_DSN=flock
|
||||||
###< symfony/lock ###
|
###< symfony/lock ###
|
||||||
|
|
||||||
###> nelmio/cors-bundle ###
|
|
||||||
CORS_ALLOW_ORIGIN='^https?://(localhost|127\.0\.0\.1)(:[0-9]+)?$'
|
|
||||||
###< nelmio/cors-bundle ###
|
|
||||||
|
|
0
.env.dev
0
.env.dev
|
@ -5,9 +5,5 @@ SYMFONY_DEPRECATIONS_HELPER=999999
|
||||||
PANTHER_APP_ENV=panther
|
PANTHER_APP_ENV=panther
|
||||||
PANTHER_ERROR_SCREENSHOT_DIR=./var/error-screenshots
|
PANTHER_ERROR_SCREENSHOT_DIR=./var/error-screenshots
|
||||||
|
|
||||||
DATABASE_URL="sqlite:///%kernel.project_dir%/var/app_test.db"
|
|
||||||
# Doctrine automatically adds an _test suffix to database name in test env
|
# Doctrine automatically adds an _test suffix to database name in test env
|
||||||
#DATABASE_URL=mysql://root:@127.0.0.1:3306/part-db
|
DATABASE_URL=mysql://root:@127.0.0.1:3306/part-db
|
||||||
|
|
||||||
# Disable update checks, as tests would fail, when github is not reachable
|
|
||||||
CHECK_FOR_UPDATES=0
|
|
18
.github/workflows/assets_artifact_build.yml
vendored
18
.github/workflows/assets_artifact_build.yml
vendored
|
@ -21,20 +21,12 @@ jobs:
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Setup PHP
|
|
||||||
uses: shivammathur/setup-php@v2
|
|
||||||
with:
|
|
||||||
php-version: '8.2'
|
|
||||||
coverage: none
|
|
||||||
ini-values: xdebug.max_nesting_level=1000
|
|
||||||
extensions: mbstring, intl, gd, xsl, gmp, bcmath, :php-psr
|
|
||||||
|
|
||||||
- name: Get Composer Cache Directory
|
- name: Get Composer Cache Directory
|
||||||
id: composer-cache
|
id: composer-cache
|
||||||
run: |
|
run: |
|
||||||
echo "::set-output name=dir::$(composer config cache-files-dir)"
|
echo "::set-output name=dir::$(composer config cache-files-dir)"
|
||||||
|
|
||||||
- uses: actions/cache@v4
|
- uses: actions/cache@v3
|
||||||
with:
|
with:
|
||||||
path: ${{ steps.composer-cache.outputs.dir }}
|
path: ${{ steps.composer-cache.outputs.dir }}
|
||||||
key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }}
|
key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }}
|
||||||
|
@ -48,7 +40,7 @@ jobs:
|
||||||
id: yarn-cache-dir-path
|
id: yarn-cache-dir-path
|
||||||
run: echo "::set-output name=dir::$(yarn cache dir)"
|
run: echo "::set-output name=dir::$(yarn cache dir)"
|
||||||
|
|
||||||
- uses: actions/cache@v4
|
- uses: actions/cache@v3
|
||||||
id: yarn-cache # use this to check for `cache-hit` (`steps.yarn-cache.outputs.cache-hit != 'true'`)
|
id: yarn-cache # use this to check for `cache-hit` (`steps.yarn-cache.outputs.cache-hit != 'true'`)
|
||||||
with:
|
with:
|
||||||
path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
|
path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
|
||||||
|
@ -57,7 +49,7 @@ jobs:
|
||||||
${{ runner.os }}-yarn-
|
${{ runner.os }}-yarn-
|
||||||
|
|
||||||
- name: Setup node
|
- name: Setup node
|
||||||
uses: actions/setup-node@v4
|
uses: actions/setup-node@v3
|
||||||
with:
|
with:
|
||||||
node-version: '18'
|
node-version: '18'
|
||||||
|
|
||||||
|
@ -77,13 +69,13 @@ jobs:
|
||||||
run: zip -r /tmp/partdb_assets.zip public/build/ vendor/
|
run: zip -r /tmp/partdb_assets.zip public/build/ vendor/
|
||||||
|
|
||||||
- name: Upload assets artifact
|
- name: Upload assets artifact
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v3
|
||||||
with:
|
with:
|
||||||
name: Only dependencies and built assets
|
name: Only dependencies and built assets
|
||||||
path: /tmp/partdb_assets.zip
|
path: /tmp/partdb_assets.zip
|
||||||
|
|
||||||
- name: Upload full artifact
|
- name: Upload full artifact
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v3
|
||||||
with:
|
with:
|
||||||
name: Full Part-DB including dependencies and built assets
|
name: Full Part-DB including dependencies and built assets
|
||||||
path: /tmp/partdb_with_assets.zip
|
path: /tmp/partdb_with_assets.zip
|
||||||
|
|
4
.github/workflows/docker_build.yml
vendored
4
.github/workflows/docker_build.yml
vendored
|
@ -58,14 +58,14 @@ jobs:
|
||||||
-
|
-
|
||||||
name: Login to DockerHub
|
name: Login to DockerHub
|
||||||
if: github.event_name != 'pull_request'
|
if: github.event_name != 'pull_request'
|
||||||
uses: docker/login-action@v3
|
uses: docker/login-action@v2
|
||||||
with:
|
with:
|
||||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||||
|
|
||||||
-
|
-
|
||||||
name: Build and push
|
name: Build and push
|
||||||
uses: docker/build-push-action@v6
|
uses: docker/build-push-action@v5
|
||||||
with:
|
with:
|
||||||
context: .
|
context: .
|
||||||
platforms: linux/amd64,linux/arm64,linux/arm/v7
|
platforms: linux/amd64,linux/arm64,linux/arm/v7
|
||||||
|
|
77
.github/workflows/docker_frankenphp.yml
vendored
77
.github/workflows/docker_frankenphp.yml
vendored
|
@ -1,77 +0,0 @@
|
||||||
name: Docker Image Build (FrankenPHP)
|
|
||||||
|
|
||||||
on:
|
|
||||||
#schedule:
|
|
||||||
# - cron: '0 10 * * *' # everyday at 10am
|
|
||||||
push:
|
|
||||||
branches:
|
|
||||||
- '**'
|
|
||||||
- '!l10n_**'
|
|
||||||
tags:
|
|
||||||
- 'v*.*.*'
|
|
||||||
- 'v*.*.*-**'
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
docker:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
-
|
|
||||||
name: Checkout
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
-
|
|
||||||
name: Docker meta
|
|
||||||
id: docker_meta
|
|
||||||
uses: docker/metadata-action@v5
|
|
||||||
with:
|
|
||||||
# list of Docker images to use as base name for tags
|
|
||||||
images: |
|
|
||||||
partdborg/part-db
|
|
||||||
# Mark the image build from master as latest (as we dont have really releases yet)
|
|
||||||
tags: |
|
|
||||||
type=edge,branch=master
|
|
||||||
type=ref,event=branch,
|
|
||||||
type=ref,event=tag,
|
|
||||||
type=schedule
|
|
||||||
type=semver,pattern={{version}}
|
|
||||||
type=semver,pattern={{major}}.{{minor}}
|
|
||||||
type=semver,pattern={{major}}
|
|
||||||
type=ref,event=branch
|
|
||||||
type=ref,event=pr
|
|
||||||
labels: |
|
|
||||||
org.opencontainers.image.source=${{ github.event.repository.clone_url }}
|
|
||||||
org.opencontainers.image.revision=${{ github.sha }}
|
|
||||||
org.opencontainers.image.title=Part-DB
|
|
||||||
org.opencontainers.image.description=Part-DB is a web application for managing electronic components and your inventory.
|
|
||||||
org.opencontainers.image.url=https://github.com/Part-DB/Part-DB-server
|
|
||||||
org.opencontainers.image.source=https://github.com/Part-DB/Part-DB-server
|
|
||||||
org.opencontainers.image.authors=Jan Böhmer
|
|
||||||
org.opencontainers.licenses=AGPL-3.0-or-later
|
|
||||||
|
|
||||||
-
|
|
||||||
name: Set up QEMU
|
|
||||||
uses: docker/setup-qemu-action@v3
|
|
||||||
with:
|
|
||||||
platforms: 'arm64,arm'
|
|
||||||
-
|
|
||||||
name: Set up Docker Buildx
|
|
||||||
uses: docker/setup-buildx-action@v3
|
|
||||||
-
|
|
||||||
name: Login to DockerHub
|
|
||||||
if: github.event_name != 'pull_request'
|
|
||||||
uses: docker/login-action@v3
|
|
||||||
with:
|
|
||||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
|
||||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
|
||||||
|
|
||||||
-
|
|
||||||
name: Build and push
|
|
||||||
uses: docker/build-push-action@v6
|
|
||||||
with:
|
|
||||||
context: .
|
|
||||||
file: Dockerfile-frankenphp
|
|
||||||
platforms: linux/amd64,linux/arm64,linux/arm/v7
|
|
||||||
push: ${{ github.event_name != 'pull_request' }}
|
|
||||||
tags: ${{ steps.docker_meta.outputs.tags }}
|
|
||||||
labels: ${{ steps.docker_meta.outputs.labels }}
|
|
||||||
cache-from: type=gha
|
|
||||||
cache-to: type=gha,mode=max
|
|
15
.github/workflows/static_analysis.yml
vendored
15
.github/workflows/static_analysis.yml
vendored
|
@ -18,20 +18,12 @@ jobs:
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Setup PHP
|
|
||||||
uses: shivammathur/setup-php@v2
|
|
||||||
with:
|
|
||||||
php-version: '8.2'
|
|
||||||
coverage: none
|
|
||||||
ini-values: xdebug.max_nesting_level=1000
|
|
||||||
extensions: mbstring, intl, gd, xsl, gmp, bcmath, :php-psr
|
|
||||||
|
|
||||||
- name: Get Composer Cache Directory
|
- name: Get Composer Cache Directory
|
||||||
id: composer-cache
|
id: composer-cache
|
||||||
run: |
|
run: |
|
||||||
echo "::set-output name=dir::$(composer config cache-files-dir)"
|
echo "::set-output name=dir::$(composer config cache-files-dir)"
|
||||||
|
|
||||||
- uses: actions/cache@v4
|
- uses: actions/cache@v3
|
||||||
with:
|
with:
|
||||||
path: ${{ steps.composer-cache.outputs.dir }}
|
path: ${{ steps.composer-cache.outputs.dir }}
|
||||||
key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }}
|
key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }}
|
||||||
|
@ -56,10 +48,9 @@ jobs:
|
||||||
|
|
||||||
- name: Check doctrine mapping
|
- name: Check doctrine mapping
|
||||||
run: ./bin/console doctrine:schema:validate --skip-sync -vvv --no-interaction
|
run: ./bin/console doctrine:schema:validate --skip-sync -vvv --no-interaction
|
||||||
|
|
||||||
# Use the -d option to raise the max nesting level
|
|
||||||
- name: Generate dev container
|
- name: Generate dev container
|
||||||
run: php -d xdebug.max_nesting_level=1000 ./bin/console cache:clear --env dev
|
run: ./bin/console cache:clear --env dev
|
||||||
|
|
||||||
- name: Run PHPstan
|
- name: Run PHPstan
|
||||||
run: composer phpstan
|
run: composer phpstan
|
||||||
|
|
55
.github/workflows/tests.yml
vendored
55
.github/workflows/tests.yml
vendored
|
@ -13,13 +13,13 @@ on:
|
||||||
jobs:
|
jobs:
|
||||||
phpunit:
|
phpunit:
|
||||||
name: PHPUnit and coverage Test (PHP ${{ matrix.php-versions }}, ${{ matrix.db-type }})
|
name: PHPUnit and coverage Test (PHP ${{ matrix.php-versions }}, ${{ matrix.db-type }})
|
||||||
|
# Ubuntu 20.04 ships MySQL 8.0 which causes problems with login, so we just use ubuntu 18.04 for now...
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ubuntu-22.04
|
||||||
|
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
|
||||||
matrix:
|
matrix:
|
||||||
php-versions: [ '8.1', '8.2', '8.3', '8.4' ]
|
php-versions: [ '8.1', '8.2' ]
|
||||||
db-type: [ 'mysql', 'sqlite', 'postgres' ]
|
db-type: [ 'mysql', 'sqlite' ]
|
||||||
|
|
||||||
env:
|
env:
|
||||||
# Note that we set DATABASE URL later based on our db-type matrix value
|
# Note that we set DATABASE URL later based on our db-type matrix value
|
||||||
|
@ -27,21 +27,16 @@ jobs:
|
||||||
SYMFONY_DEPRECATIONS_HELPER: disabled
|
SYMFONY_DEPRECATIONS_HELPER: disabled
|
||||||
PHP_VERSION: ${{ matrix.php-versions }}
|
PHP_VERSION: ${{ matrix.php-versions }}
|
||||||
DB_TYPE: ${{ matrix.db-type }}
|
DB_TYPE: ${{ matrix.db-type }}
|
||||||
CHECK_FOR_UPDATES: false # Disable update checks for tests
|
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Set Database env for MySQL
|
- name: Set Database env for MySQL
|
||||||
run: echo "DATABASE_URL=mysql://root:root@127.0.0.1:3306/partdb?serverVersion=8.0.35" >> $GITHUB_ENV
|
run: echo "DATABASE_URL=mysql://root:root@127.0.0.1:3306/test" >> $GITHUB_ENV
|
||||||
if: matrix.db-type == 'mysql'
|
if: matrix.db-type == 'mysql'
|
||||||
|
|
||||||
- name: Set Database env for SQLite
|
- name: Set Database env for SQLite
|
||||||
run: echo "DATABASE_URL="sqlite:///%kernel.project_dir%/var/app_test.db"" >> $GITHUB_ENV
|
run: echo "DATABASE_URL="sqlite:///%kernel.project_dir%/var/app_test.db"" >> $GITHUB_ENV
|
||||||
if: matrix.db-type == 'sqlite'
|
if: matrix.db-type == 'sqlite'
|
||||||
|
|
||||||
- name: Set Database env for PostgreSQL
|
|
||||||
run: echo "DATABASE_URL=postgresql://postgres:postgres @127.0.0.1:5432/partdb?serverVersion=14&charset=utf8" >> $GITHUB_ENV
|
|
||||||
if: matrix.db-type == 'postgres'
|
|
||||||
|
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
@ -50,22 +45,12 @@ jobs:
|
||||||
with:
|
with:
|
||||||
php-version: ${{ matrix.php-versions }}
|
php-version: ${{ matrix.php-versions }}
|
||||||
coverage: pcov
|
coverage: pcov
|
||||||
ini-values: xdebug.max_nesting_level=1000
|
extensions: mbstring, intl, gd, xsl, gmp, bcmath
|
||||||
extensions: mbstring, intl, gd, xsl, gmp, bcmath, :php-psr
|
|
||||||
|
|
||||||
- name: Start MySQL
|
- name: Start MySQL
|
||||||
run: sudo systemctl start mysql.service
|
run: sudo systemctl start mysql.service
|
||||||
if: matrix.db-type == 'mysql'
|
|
||||||
|
|
||||||
# Replace the scram-sha-256 with trust for host connections, to avoid password authentication
|
#- name: Setup MySQL
|
||||||
- name: Start PostgreSQL
|
|
||||||
run: |
|
|
||||||
sudo sed -i 's/^\(host.*all.*all.*\)scram-sha-256/\1trust/' /etc/postgresql/14/main/pg_hba.conf
|
|
||||||
sudo systemctl start postgresql.service
|
|
||||||
sudo -u postgres psql -c "ALTER USER postgres PASSWORD 'postgres';"
|
|
||||||
if: matrix.db-type == 'postgres'
|
|
||||||
|
|
||||||
#- name: Setup MySQL
|
|
||||||
# uses: mirromutth/mysql-action@v1.1
|
# uses: mirromutth/mysql-action@v1.1
|
||||||
# with:
|
# with:
|
||||||
# mysql version: 5.7
|
# mysql version: 5.7
|
||||||
|
@ -78,7 +63,7 @@ jobs:
|
||||||
id: composer-cache
|
id: composer-cache
|
||||||
run: |
|
run: |
|
||||||
echo "::set-output name=dir::$(composer config cache-files-dir)"
|
echo "::set-output name=dir::$(composer config cache-files-dir)"
|
||||||
- uses: actions/cache@v4
|
- uses: actions/cache@v3
|
||||||
with:
|
with:
|
||||||
path: ${{ steps.composer-cache.outputs.dir }}
|
path: ${{ steps.composer-cache.outputs.dir }}
|
||||||
key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }}
|
key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }}
|
||||||
|
@ -89,7 +74,7 @@ jobs:
|
||||||
id: yarn-cache-dir-path
|
id: yarn-cache-dir-path
|
||||||
run: echo "::set-output name=dir::$(yarn cache dir)"
|
run: echo "::set-output name=dir::$(yarn cache dir)"
|
||||||
|
|
||||||
- uses: actions/cache@v4
|
- uses: actions/cache@v3
|
||||||
id: yarn-cache # use this to check for `cache-hit` (`steps.yarn-cache.outputs.cache-hit != 'true'`)
|
id: yarn-cache # use this to check for `cache-hit` (`steps.yarn-cache.outputs.cache-hit != 'true'`)
|
||||||
with:
|
with:
|
||||||
path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
|
path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
|
||||||
|
@ -101,7 +86,7 @@ jobs:
|
||||||
run: composer install --prefer-dist --no-progress
|
run: composer install --prefer-dist --no-progress
|
||||||
|
|
||||||
- name: Setup node
|
- name: Setup node
|
||||||
uses: actions/setup-node@v4
|
uses: actions/setup-node@v3
|
||||||
with:
|
with:
|
||||||
node-version: '18'
|
node-version: '18'
|
||||||
|
|
||||||
|
@ -113,24 +98,26 @@ jobs:
|
||||||
|
|
||||||
- name: Create DB
|
- name: Create DB
|
||||||
run: php bin/console --env test doctrine:database:create --if-not-exists -n
|
run: php bin/console --env test doctrine:database:create --if-not-exists -n
|
||||||
if: matrix.db-type == 'mysql' || matrix.db-type == 'postgres'
|
if: matrix.db-type == 'mysql'
|
||||||
|
|
||||||
|
# Checkinf for existance is not supported for sqlite, so do it without it
|
||||||
|
- name: Create DB
|
||||||
|
run: php bin/console --env test doctrine:database:create -n
|
||||||
|
if: matrix.db-type == 'sqlite'
|
||||||
|
|
||||||
- name: Do migrations
|
- name: Do migrations
|
||||||
run: php bin/console --env test doctrine:migrations:migrate -n
|
run: php bin/console --env test doctrine:migrations:migrate -n
|
||||||
|
|
||||||
# Use our own custom fixtures loading command to circumvent some problems with reset the autoincrement values
|
|
||||||
- name: Load fixtures
|
- name: Load fixtures
|
||||||
run: php bin/console --env test partdb:fixtures:load -n
|
run: php bin/console --env test doctrine:fixtures:load -n
|
||||||
|
|
||||||
- name: Run PHPunit and generate coverage
|
- name: Run PHPunit and generate coverage
|
||||||
run: ./bin/phpunit --coverage-clover=coverage.xml
|
run: ./bin/phpunit --coverage-clover=coverage.xml
|
||||||
|
|
||||||
- name: Upload coverage
|
- name: Upload coverage
|
||||||
uses: codecov/codecov-action@v5
|
uses: codecov/codecov-action@v3
|
||||||
with:
|
with:
|
||||||
env_vars: PHP_VERSION,DB_TYPE
|
env_vars: PHP_VERSION,DB_TYPE
|
||||||
token: ${{ secrets.CODECOV_TOKEN }}
|
|
||||||
fail_ci_if_error: true
|
|
||||||
|
|
||||||
- name: Test app:clean-attachments
|
- name: Test app:clean-attachments
|
||||||
run: php bin/console partdb:attachments:clean-unused -n
|
run: php bin/console partdb:attachments:clean-unused -n
|
||||||
|
@ -144,11 +131,11 @@ jobs:
|
||||||
- name: Test check-requirements command
|
- name: Test check-requirements command
|
||||||
run: php bin/console partdb:check-requirements -n
|
run: php bin/console partdb:check-requirements -n
|
||||||
|
|
||||||
- name: Test partdb:backup command
|
|
||||||
run: php bin/console partdb:backup -n --full /tmp/test_backup.zip
|
|
||||||
|
|
||||||
- name: Test legacy Part-DB import
|
- name: Test legacy Part-DB import
|
||||||
run: bash .github/assets/legacy_import/test_legacy_import.sh
|
run: bash .github/assets/legacy_import/test_legacy_import.sh
|
||||||
if: matrix.db-type == 'mysql' && matrix.php-versions == '8.2'
|
if: matrix.db-type == 'mysql' && matrix.php-versions == '8.2'
|
||||||
env:
|
env:
|
||||||
DATABASE_URL: mysql://root:root@localhost:3306/legacy_db
|
DATABASE_URL: mysql://root:root@localhost:3306/legacy_db
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
4
.gitignore
vendored
4
.gitignore
vendored
|
@ -43,7 +43,3 @@ yarn-error.log
|
||||||
/phpunit.xml
|
/phpunit.xml
|
||||||
.phpunit.result.cache
|
.phpunit.result.cache
|
||||||
###< phpunit/phpunit ###
|
###< phpunit/phpunit ###
|
||||||
|
|
||||||
###> phpstan/phpstan ###
|
|
||||||
phpstan.neon
|
|
||||||
###< phpstan/phpstan ###
|
|
||||||
|
|
187
Dockerfile
187
Dockerfile
|
@ -1,64 +1,22 @@
|
||||||
ARG BASE_IMAGE=debian:bookworm-slim
|
FROM debian:bullseye-slim
|
||||||
ARG PHP_VERSION=8.3
|
|
||||||
|
|
||||||
FROM ${BASE_IMAGE} AS base
|
|
||||||
ARG PHP_VERSION
|
|
||||||
|
|
||||||
# Install needed dependencies for PHP build
|
# Install needed dependencies for PHP build
|
||||||
#RUN apt-get update && apt-get install -y pkg-config curl libcurl4-openssl-dev libicu-dev \
|
#RUN apt-get update && apt-get install -y pkg-config curl libcurl4-openssl-dev libicu-dev \
|
||||||
# libpng-dev libjpeg-dev libfreetype6-dev gnupg zip libzip-dev libjpeg62-turbo-dev libonig-dev libxslt-dev libwebp-dev vim \
|
# libpng-dev libjpeg-dev libfreetype6-dev gnupg zip libzip-dev libjpeg62-turbo-dev libonig-dev libxslt-dev libwebp-dev vim \
|
||||||
# && apt-get -y autoremove && apt-get clean autoclean && rm -rf /var/lib/apt/lists/*
|
# && apt-get -y autoremove && apt-get clean autoclean && rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
RUN apt-get update && apt-get -y install \
|
RUN apt-get update && apt-get -y install apt-transport-https lsb-release ca-certificates curl zip \
|
||||||
apt-transport-https \
|
|
||||||
lsb-release \
|
|
||||||
ca-certificates \
|
|
||||||
curl \
|
|
||||||
zip \
|
|
||||||
mariadb-client \
|
|
||||||
postgresql-client \
|
|
||||||
&& curl -sSLo /usr/share/keyrings/deb.sury.org-php.gpg https://packages.sury.org/php/apt.gpg \
|
&& curl -sSLo /usr/share/keyrings/deb.sury.org-php.gpg https://packages.sury.org/php/apt.gpg \
|
||||||
&& sh -c 'echo "deb [signed-by=/usr/share/keyrings/deb.sury.org-php.gpg] https://packages.sury.org/php/ $(lsb_release -sc) main" > /etc/apt/sources.list.d/php.list' \
|
&& sh -c 'echo "deb [signed-by=/usr/share/keyrings/deb.sury.org-php.gpg] https://packages.sury.org/php/ $(lsb_release -sc) main" > /etc/apt/sources.list.d/php.list' \
|
||||||
&& apt-get update && apt-get upgrade -y \
|
&& apt-get update && apt-get upgrade -y \
|
||||||
&& apt-get install -y \
|
&& apt-get install -y apache2 php8.1 php8.1-fpm php8.1-opcache php8.1-curl php8.1-gd php8.1-mbstring php8.1-xml php8.1-bcmath php8.1-intl php8.1-zip php8.1-xsl php8.1-sqlite3 php8.1-mysql gpg \
|
||||||
apache2 \
|
&& apt-get -y autoremove && apt-get clean autoclean && rm -rf /var/lib/apt/lists/*;
|
||||||
php${PHP_VERSION} \
|
|
||||||
php${PHP_VERSION}-fpm \
|
ENV APACHE_CONFDIR /etc/apache2
|
||||||
php${PHP_VERSION}-opcache \
|
ENV APACHE_ENVVARS $APACHE_CONFDIR/envvars
|
||||||
php${PHP_VERSION}-curl \
|
|
||||||
php${PHP_VERSION}-gd \
|
|
||||||
php${PHP_VERSION}-mbstring \
|
|
||||||
php${PHP_VERSION}-xml \
|
|
||||||
php${PHP_VERSION}-bcmath \
|
|
||||||
php${PHP_VERSION}-intl \
|
|
||||||
php${PHP_VERSION}-zip \
|
|
||||||
php${PHP_VERSION}-xsl \
|
|
||||||
php${PHP_VERSION}-sqlite3 \
|
|
||||||
php${PHP_VERSION}-mysql \
|
|
||||||
php${PHP_VERSION}-pgsql \
|
|
||||||
gpg \
|
|
||||||
sudo \
|
|
||||||
&& apt-get -y autoremove && apt-get clean autoclean && rm -rf /var/lib/apt/lists/* \
|
|
||||||
# Create workdir and set permissions if directory does not exists
|
# Create workdir and set permissions if directory does not exists
|
||||||
&& mkdir -p /var/www/html \
|
RUN mkdir -p /var/www/html && chown -R www-data:www-data /var/www/html
|
||||||
&& chown -R www-data:www-data /var/www/html \
|
|
||||||
# delete the "index.html" that installing Apache drops in here
|
|
||||||
&& rm -rvf /var/www/html/*
|
|
||||||
|
|
||||||
# Install node and yarn
|
|
||||||
RUN curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add - && \
|
|
||||||
echo "deb https://dl.yarnpkg.com/debian/ stable main" | tee /etc/apt/sources.list.d/yarn.list && \
|
|
||||||
curl -sL https://deb.nodesource.com/setup_20.x | bash - && \
|
|
||||||
apt-get update && apt-get install -y \
|
|
||||||
nodejs \
|
|
||||||
yarn \
|
|
||||||
&& apt-get -y autoremove && apt-get clean autoclean && rm -rf /var/lib/apt/lists/*
|
|
||||||
|
|
||||||
# Install composer
|
|
||||||
COPY --from=composer:latest /usr/bin/composer /usr/bin/composer
|
|
||||||
|
|
||||||
ENV APACHE_CONFDIR=/etc/apache2
|
|
||||||
ENV APACHE_ENVVARS=$APACHE_CONFDIR/envvars
|
|
||||||
|
|
||||||
# Configure apache 2 (taken from https://github.com/docker-library/php/blob/master/8.2/bullseye/apache/Dockerfile)
|
# Configure apache 2 (taken from https://github.com/docker-library/php/blob/master/8.2/bullseye/apache/Dockerfile)
|
||||||
# generically convert lines like
|
# generically convert lines like
|
||||||
|
@ -69,6 +27,8 @@ ENV APACHE_ENVVARS=$APACHE_CONFDIR/envvars
|
||||||
# so that they can be overridden at runtime ("-e APACHE_RUN_USER=...")
|
# so that they can be overridden at runtime ("-e APACHE_RUN_USER=...")
|
||||||
RUN sed -ri 's/^export ([^=]+)=(.*)$/: ${\1:=\2}\nexport \1/' "$APACHE_ENVVARS"; \
|
RUN sed -ri 's/^export ([^=]+)=(.*)$/: ${\1:=\2}\nexport \1/' "$APACHE_ENVVARS"; \
|
||||||
set -eux; . "$APACHE_ENVVARS"; \
|
set -eux; . "$APACHE_ENVVARS"; \
|
||||||
|
# delete the "index.html" that installing Apache drops in here
|
||||||
|
rm -rvf /var/www/html/*; \
|
||||||
\
|
\
|
||||||
# logs should go to stdout / stderr
|
# logs should go to stdout / stderr
|
||||||
ln -sfT /dev/stderr "$APACHE_LOG_DIR/error.log"; \
|
ln -sfT /dev/stderr "$APACHE_LOG_DIR/error.log"; \
|
||||||
|
@ -76,87 +36,82 @@ RUN sed -ri 's/^export ([^=]+)=(.*)$/: ${\1:=\2}\nexport \1/' "$APACHE_ENVVARS"
|
||||||
ln -sfT /dev/stdout "$APACHE_LOG_DIR/other_vhosts_access.log"; \
|
ln -sfT /dev/stdout "$APACHE_LOG_DIR/other_vhosts_access.log"; \
|
||||||
chown -R --no-dereference "$APACHE_RUN_USER:$APACHE_RUN_GROUP" "$APACHE_LOG_DIR";
|
chown -R --no-dereference "$APACHE_RUN_USER:$APACHE_RUN_GROUP" "$APACHE_LOG_DIR";
|
||||||
|
|
||||||
# ---
|
# Enable php-fpm
|
||||||
|
RUN a2enmod proxy_fcgi setenvif && a2enconf php8.1-fpm
|
||||||
|
|
||||||
FROM scratch AS apache-config
|
|
||||||
ARG PHP_VERSION
|
|
||||||
# Configure php-fpm to log to stdout of the container (stdout of PID 1)
|
# Configure php-fpm to log to stdout of the container (stdout of PID 1)
|
||||||
# We have to use /proc/1/fd/1 because /dev/stdout or /proc/self/fd/1 does not point to the container stdout (because we use apache as entrypoint)
|
# We have to use /proc/1/fd/1 because /dev/stdout or /proc/self/fd/1 does not point to the container stdout (because we use apache as entrypoint)
|
||||||
# We also disable the clear_env option to allow the use of environment variables in php-fpm
|
# We also disable the clear_env option to allow the use of environment variables in php-fpm
|
||||||
COPY <<EOF /etc/php/${PHP_VERSION}/fpm/pool.d/zz-docker.conf
|
RUN { \
|
||||||
[global]
|
echo '[global]'; \
|
||||||
error_log = /proc/1/fd/1
|
echo 'error_log = /proc/1/fd/1'; \
|
||||||
|
echo; \
|
||||||
[www]
|
echo '[www]'; \
|
||||||
access.log = /proc/1/fd/1
|
echo 'access.log = /proc/1/fd/1'; \
|
||||||
catch_workers_output = yes
|
echo 'catch_workers_output = yes'; \
|
||||||
decorate_workers_output = no
|
echo 'decorate_workers_output = no'; \
|
||||||
clear_env = no
|
echo 'clear_env = no'; \
|
||||||
EOF
|
} | tee "/etc/php/8.1/fpm/pool.d/zz-docker.conf"
|
||||||
|
|
||||||
# PHP files should be handled by PHP, and should be preferred over any other file type
|
# PHP files should be handled by PHP, and should be preferred over any other file type
|
||||||
COPY <<EOF /etc/apache2/conf-available/docker-php.conf
|
RUN { \
|
||||||
<FilesMatch \\.php$>
|
echo '<FilesMatch \.php$>'; \
|
||||||
SetHandler application/x-httpd-php
|
echo '\tSetHandler application/x-httpd-php'; \
|
||||||
</FilesMatch>
|
echo '</FilesMatch>'; \
|
||||||
|
echo; \
|
||||||
DirectoryIndex disabled
|
echo 'DirectoryIndex disabled'; \
|
||||||
DirectoryIndex index.php index.html
|
echo 'DirectoryIndex index.php index.html'; \
|
||||||
|
echo; \
|
||||||
<Directory /var/www/>
|
echo '<Directory /var/www/>'; \
|
||||||
Options -Indexes
|
echo '\tOptions -Indexes'; \
|
||||||
AllowOverride All
|
echo '\tAllowOverride All'; \
|
||||||
</Directory>
|
echo '</Directory>'; \
|
||||||
EOF
|
} | tee "$APACHE_CONFDIR/conf-available/docker-php.conf" \
|
||||||
|
&& a2enconf docker-php
|
||||||
|
|
||||||
# Enable opcache and configure it recommended for symfony (see https://symfony.com/doc/current/performance.html)
|
# Enable opcache and configure it recommended for symfony (see https://symfony.com/doc/current/performance.html)
|
||||||
COPY <<EOF /etc/php/${PHP_VERSION}/fpm/conf.d/symfony-recommended.ini
|
RUN \
|
||||||
opcache.memory_consumption=256
|
{ \
|
||||||
opcache.max_accelerated_files=20000
|
echo 'opcache.memory_consumption=256'; \
|
||||||
opcache.validate_timestamp=0
|
echo 'opcache.max_accelerated_files=20000'; \
|
||||||
# Configure Realpath cache for performance
|
echo 'opcache.validate_timestamp=0'; \
|
||||||
realpath_cache_size=4096K
|
# Configure Realpath cache for performance
|
||||||
realpath_cache_ttl=600
|
echo 'realpath_cache_size=4096K'; \
|
||||||
EOF
|
echo 'realpath_cache_ttl=600'; \
|
||||||
|
} > /etc/php/8.1/fpm/conf.d/symfony-recommended.ini
|
||||||
|
|
||||||
# Increase upload limit and enable preloading
|
# Increase upload limit and enable preloading
|
||||||
COPY <<EOF /etc/php/${PHP_VERSION}/fpm/conf.d/partdb.ini
|
RUN \
|
||||||
upload_max_filesize=256M
|
{ \
|
||||||
post_max_size=300M
|
echo 'upload_max_filesize=256M'; \
|
||||||
opcache.preload_user=www-data
|
echo 'post_max_size=300M'; \
|
||||||
opcache.preload=/var/www/html/config/preload.php
|
echo 'opcache.preload_user=www-data'; \
|
||||||
log_limit=8096
|
echo 'opcache.preload=/var/www/html/config/preload.php'; \
|
||||||
EOF
|
} > /etc/php/8.1/fpm/conf.d/partdb.ini
|
||||||
|
|
||||||
COPY ./.docker/symfony.conf /etc/apache2/sites-available/symfony.conf
|
# Install node and yarn
|
||||||
|
RUN curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add -
|
||||||
|
RUN echo "deb https://dl.yarnpkg.com/debian/ stable main" | tee /etc/apt/sources.list.d/yarn.list
|
||||||
|
RUN curl -sL https://deb.nodesource.com/setup_18.x | bash - && apt-get update && apt-get install -y nodejs yarn && apt-get -y autoremove && apt-get clean autoclean && rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
# ---
|
# Install composer
|
||||||
|
COPY --from=composer:latest /usr/bin/composer /usr/bin/composer
|
||||||
|
|
||||||
FROM base
|
|
||||||
ARG PHP_VERSION
|
|
||||||
|
|
||||||
# Set working dir
|
# Set working dir
|
||||||
WORKDIR /var/www/html
|
WORKDIR /var/www/html
|
||||||
COPY --from=apache-config / /
|
|
||||||
COPY --chown=www-data:www-data . .
|
COPY --chown=www-data:www-data . .
|
||||||
|
|
||||||
# Setup apache2
|
# Setup apache2
|
||||||
RUN a2dissite 000-default.conf && \
|
RUN a2dissite 000-default.conf
|
||||||
a2ensite symfony.conf && \
|
COPY ./.docker/symfony.conf /etc/apache2/sites-available/symfony.conf
|
||||||
# Enable php-fpm
|
RUN a2ensite symfony.conf
|
||||||
a2enmod proxy_fcgi setenvif && \
|
RUN a2enmod rewrite
|
||||||
a2enconf php${PHP_VERSION}-fpm && \
|
|
||||||
a2enconf docker-php && \
|
|
||||||
a2enmod rewrite
|
|
||||||
|
|
||||||
# Install composer and yarn dependencies for Part-DB
|
# Install composer and yarn dependencies for Part-DB
|
||||||
USER www-data
|
USER www-data
|
||||||
RUN composer install -a --no-dev && \
|
RUN composer install -a --no-dev && composer clear-cache
|
||||||
composer clear-cache
|
RUN yarn install --network-timeout 600000 && yarn build && yarn cache clean && rm -rf node_modules/
|
||||||
RUN yarn install --network-timeout 600000 && \
|
|
||||||
yarn build && \
|
|
||||||
yarn cache clean && \
|
|
||||||
rm -rf node_modules/
|
|
||||||
|
|
||||||
# Use docker env to output logs to stdout
|
# Use docker env to output logs to stdout
|
||||||
ENV APP_ENV=docker
|
ENV APP_ENV=docker
|
||||||
|
@ -164,12 +119,10 @@ ENV DATABASE_URL="sqlite:///%kernel.project_dir%/uploads/app.db"
|
||||||
|
|
||||||
USER root
|
USER root
|
||||||
|
|
||||||
# Replace the php version placeholder in the entry point, with our php version
|
# Copy entrypoint to /usr/local/bin and make it executable
|
||||||
RUN sed -i "s/PHP_VERSION/${PHP_VERSION}/g" ./.docker/partdb-entrypoint.sh
|
RUN cp ./.docker/partdb-entrypoint.sh /usr/local/bin/partdb-entrypoint.sh && chmod +x /usr/local/bin/partdb-entrypoint.sh
|
||||||
|
# Copy apache2-foreground to /usr/local/bin and make it executable
|
||||||
# Copy entrypoint and apache2-foreground to /usr/local/bin and make it executable
|
RUN cp ./.docker/apache2-foreground /usr/local/bin/apache2-foreground && chmod +x /usr/local/bin/apache2-foreground
|
||||||
RUN install ./.docker/partdb-entrypoint.sh /usr/local/bin && \
|
|
||||||
install ./.docker/apache2-foreground /usr/local/bin
|
|
||||||
ENTRYPOINT ["partdb-entrypoint.sh"]
|
ENTRYPOINT ["partdb-entrypoint.sh"]
|
||||||
CMD ["apache2-foreground"]
|
CMD ["apache2-foreground"]
|
||||||
|
|
||||||
|
@ -177,4 +130,4 @@ CMD ["apache2-foreground"]
|
||||||
STOPSIGNAL SIGWINCH
|
STOPSIGNAL SIGWINCH
|
||||||
|
|
||||||
EXPOSE 80
|
EXPOSE 80
|
||||||
VOLUME ["/var/www/html/uploads", "/var/www/html/public/media"]
|
VOLUME ["/var/www/html/uploads", "/var/www/html/public/media"]
|
|
@ -1,101 +0,0 @@
|
||||||
FROM dunglas/frankenphp:1-php8.3 AS frankenphp_upstream
|
|
||||||
|
|
||||||
RUN apt-get update && apt-get -y install \
|
|
||||||
curl \
|
|
||||||
ca-certificates \
|
|
||||||
mariadb-client \
|
|
||||||
postgresql-client \
|
|
||||||
file \
|
|
||||||
acl \
|
|
||||||
git \
|
|
||||||
gettext \
|
|
||||||
gnupg \
|
|
||||||
zip \
|
|
||||||
&& apt-get -y autoremove && apt-get clean autoclean && rm -rf /var/lib/apt/lists/*;
|
|
||||||
|
|
||||||
# Install node and yarn
|
|
||||||
RUN curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add - && \
|
|
||||||
echo "deb https://dl.yarnpkg.com/debian/ stable main" | tee /etc/apt/sources.list.d/yarn.list && \
|
|
||||||
curl -sL https://deb.nodesource.com/setup_20.x | bash - && \
|
|
||||||
apt-get update && apt-get install -y \
|
|
||||||
nodejs yarn \
|
|
||||||
&& apt-get -y autoremove && apt-get clean autoclean && rm -rf /var/lib/apt/lists/*
|
|
||||||
|
|
||||||
# Install PHP
|
|
||||||
RUN set -eux; \
|
|
||||||
install-php-extensions \
|
|
||||||
@composer \
|
|
||||||
apcu \
|
|
||||||
intl \
|
|
||||||
opcache \
|
|
||||||
zip \
|
|
||||||
pdo_mysql \
|
|
||||||
pdo_sqlite \
|
|
||||||
pdo_pgsql \
|
|
||||||
gd \
|
|
||||||
bcmath \
|
|
||||||
xsl \
|
|
||||||
;
|
|
||||||
|
|
||||||
# Copy config files for php and caddy
|
|
||||||
COPY --link .docker/frankenphp/conf.d/app.ini $PHP_INI_DIR/conf.d/
|
|
||||||
COPY --chmod=755 .docker/frankenphp/docker-entrypoint.sh /usr/local/bin/docker-entrypoint
|
|
||||||
COPY --link .docker/frankenphp/Caddyfile /etc/caddy/Caddyfile
|
|
||||||
COPY --link .docker/frankenphp/conf.d/app.prod.ini $PHP_INI_DIR/conf.d/
|
|
||||||
COPY --link .docker/frankenphp/worker.Caddyfile /etc/caddy/worker.Caddyfile
|
|
||||||
ENV FRANKENPHP_CONFIG="import worker.Caddyfile"
|
|
||||||
|
|
||||||
RUN mv "$PHP_INI_DIR/php.ini-production" "$PHP_INI_DIR/php.ini"
|
|
||||||
|
|
||||||
# Install composer
|
|
||||||
ENV COMPOSER_ALLOW_SUPERUSER=1
|
|
||||||
#COPY --from=composer:latest /usr/bin/composer /usr/bin/composer
|
|
||||||
|
|
||||||
# Create workdir and set permissions if directory does not exists
|
|
||||||
WORKDIR /app
|
|
||||||
|
|
||||||
# prevent the reinstallation of vendors at every changes in the source code
|
|
||||||
COPY --link composer.* symfony.* ./
|
|
||||||
RUN set -eux; \
|
|
||||||
composer install --no-cache --prefer-dist --no-dev --no-autoloader --no-scripts --no-progress
|
|
||||||
|
|
||||||
# copy sources
|
|
||||||
COPY --link . ./
|
|
||||||
|
|
||||||
# Install composer and yarn dependencies for Part-DB
|
|
||||||
RUN set -eux; \
|
|
||||||
mkdir -p var/cache var/log; \
|
|
||||||
composer dump-autoload --classmap-authoritative --no-dev; \
|
|
||||||
composer dump-env prod; \
|
|
||||||
composer run-script --no-dev post-install-cmd; \
|
|
||||||
chmod +x bin/console; sync;
|
|
||||||
|
|
||||||
RUN yarn install --network-timeout 600000 && \
|
|
||||||
yarn build && \
|
|
||||||
yarn cache clean && \
|
|
||||||
rm -rf node_modules/
|
|
||||||
|
|
||||||
# Use docker env to output logs to stdout
|
|
||||||
ENV APP_ENV=docker
|
|
||||||
ENV DATABASE_URL="sqlite:///%kernel.project_dir%/uploads/app.db"
|
|
||||||
|
|
||||||
USER root
|
|
||||||
|
|
||||||
ENTRYPOINT ["docker-entrypoint"]
|
|
||||||
CMD ["frankenphp", "run", "--config", "/etc/caddy/Caddyfile"]
|
|
||||||
|
|
||||||
# https://httpd.apache.org/docs/2.4/stopping.html#gracefulstop
|
|
||||||
STOPSIGNAL SIGWINCH
|
|
||||||
|
|
||||||
VOLUME ["/var/www/html/uploads", "/var/www/html/public/media"]
|
|
||||||
|
|
||||||
HEALTHCHECK --start-period=60s CMD curl -f http://localhost:2019/metrics || exit 1
|
|
||||||
|
|
||||||
# See https://caddyserver.com/docs/conventions#file-locations for details
|
|
||||||
ENV XDG_CONFIG_HOME /config
|
|
||||||
ENV XDG_DATA_HOME /data
|
|
||||||
|
|
||||||
EXPOSE 80
|
|
||||||
EXPOSE 443
|
|
||||||
EXPOSE 443/udp
|
|
||||||
EXPOSE 2019
|
|
150
README.md
150
README.md
|
@ -1,7 +1,7 @@
|
||||||
[](https://scrutinizer-ci.com/g/Part-DB/Part-DB-symfony/?branch=master)
|
[](https://scrutinizer-ci.com/g/Part-DB/Part-DB-symfony/?branch=master)
|
||||||

|

|
||||||

|

|
||||||
[](https://codecov.io/gh/Part-DB/Part-DB-server)
|
[](https://codecov.io/gh/Part-DB/Part-DB-server)
|
||||||

|

|
||||||

|

|
||||||
|
|
||||||
|
@ -9,156 +9,120 @@
|
||||||

|

|
||||||
[](https://part-db.crowdin.com/part-db)
|
[](https://part-db.crowdin.com/part-db)
|
||||||
|
|
||||||
**[Documentation](https://docs.part-db.de/)** | **[Demo](https://demo.part-db.de/)** | **[Docker Image](https://hub.docker.com/r/jbtronics/part-db1)**
|
**[Documentation](https://docs.part-db.de/)** | **[Demo](https://part-db.herokuapp.com)** | **[Docker Image](https://hub.docker.com/r/jbtronics/part-db1)**
|
||||||
|
|
||||||
# Part-DB
|
# Part-DB
|
||||||
|
Part-DB is an Open-Source inventory managment system for your electronic components.
|
||||||
Part-DB is an Open-Source inventory management system for your electronic components.
|
|
||||||
It is installed on a web server and so can be accessed with any browser without the need to install additional software.
|
It is installed on a web server and so can be accessed with any browser without the need to install additional software.
|
||||||
|
|
||||||
The version in this repository is a complete rewrite of the legacy [Part-DB](https://github.com/Part-DB/Part-DB)
|
The version in this Repository is a complete rewrite of the legacy [Part-DB](https://github.com/Part-DB/Part-DB) (Version < 1.0) based on a modern framework.
|
||||||
(Version < 1.0) based on a modern framework and is the recommended version to use.
|
Currently, it is still missing some (minor) features from the old version (see [UPGRADE.md](https://docs.part-db.de/upgrade_legacy.html)) for more details, but also many huge improvements and advantages compared to the old version.
|
||||||
|
If you start completely new with Part-DB it is recommended that you use the version from this repository, as it is actively developed.
|
||||||
|
|
||||||
If you find a bug, please open an [Issue on GitHub,](https://github.com/Part-DB/Part-DB-server/issues) so it can be fixed
|
If you find a bug, please open an [Issue on Github](https://github.com/Part-DB/Part-DB-server/issues) so it can be fixed for everybody.
|
||||||
for everybody.
|
|
||||||
|
|
||||||
## Demo
|
## Demo
|
||||||
|
If you want to test Part-DB without installing it, you can use [this](https://part-db.herokuapp.com) Heroku instance.
|
||||||
If you want to test Part-DB without installing it, you can use [this](https://demo.part-db.de/) Heroku instance.
|
(Or this link for the [German Version](https://part-db.herokuapp.com/de/)).
|
||||||
(Or this link for the [German Version](https://demo.part-db.de/de/)).
|
|
||||||
|
|
||||||
You can log in with username: *user* and password: *user*.
|
You can log in with username: *user* and password: *user*.
|
||||||
|
|
||||||
Every change to the master branch gets automatically deployed, so it represents the current development progress and is
|
Every change to the master branch gets automatically deployed, so it represents the current development progress and is
|
||||||
may not completely stable. Please mind, that the free Heroku instance is used, so it can take some time when loading
|
maybe not completely stable. Please mind, that the free Heroku instance is used, so it can take some time when loading the page
|
||||||
the page
|
|
||||||
for the first time.
|
for the first time.
|
||||||
|
|
||||||
<img src="https://github.com/Part-DB/Part-DB-server/raw/master/docs/assets/readme/part_info.png">
|
<img src="https://github.com/Part-DB/Part-DB-server/raw/master/docs/assets/readme/part_info.png">
|
||||||
<img src="https://github.com/Part-DB/Part-DB-server/raw/master/docs/assets/readme/parts_list.png">
|
<img src="https://github.com/Part-DB/Part-DB-server/raw/master/docs/assets/readme/parts_list.png">
|
||||||
|
|
||||||
## Features
|
## Features
|
||||||
|
* Inventory management of your electronic parts. Each part can be assigned to a category, footprint, manufacturer
|
||||||
* Inventory management of your electronic parts. Each part can be assigned to a category, footprint, manufacturer,
|
and multiple store locations and price information. Parts can be grouped using tags. You can associate various files like datasheets or pictures with the parts.
|
||||||
and multiple store locations and price information. Parts can be grouped using tags. You can associate various files
|
* Multi-Language support (currently German, English, Russian, Japanese and French (experimental))
|
||||||
like datasheets or pictures with the parts.
|
|
||||||
* Multi-language support (currently German, English, Russian, Japanese, French, Czech, Danish, and Chinese)
|
|
||||||
* Barcodes/Labels generator for parts and storage locations, scan barcodes via webcam using the builtin barcode scanner
|
* Barcodes/Labels generator for parts and storage locations, scan barcodes via webcam using the builtin barcode scanner
|
||||||
* User system with groups and detailed (fine granular) permissions.
|
* User system with groups and detailed (fine granular) permissions.
|
||||||
Two-factor authentication is supported (Google Authenticator and Webauthn/U2F keys) and can be enforced for groups.
|
Two-factor authentication is supported (Google Authenticator and Webauthn/U2F keys) and can be enforced for groups. Password reset via email can be setuped.
|
||||||
Password reset via email can be set up.
|
* Optional support for single sign-on (SSO) via SAML (using an intermediate service like [Keycloak](https://www.keycloak.org/) you can connect Part-DB to an existing LDAP or Active Directory server)
|
||||||
* Optional support for single sign-on (SSO) via SAML (using an intermediate service
|
* Import/Export system for parts and datastructure. BOM import for projects from KiCAD is supported.
|
||||||
like [Keycloak](https://www.keycloak.org/) you can connect Part-DB to an existing LDAP or Active Directory server)
|
* Project management: Create projects and assign parts to the bill of material (BOM), to show how often you could build this project and directly withdraw all components needed from DB
|
||||||
* Import/Export system for parts and data structure. BOM import for projects from KiCAD is supported.
|
* Event log: Track what changes happens to your inventory, track which user does what. Revert your parts to older versions.
|
||||||
* Project management: Create projects and assign parts to the bill of material (BOM), to show how often you could build
|
* Responsive design: You can use Part-DB on your PC, your tablet and your smartphone using the same interface.
|
||||||
this project and directly withdraw all components needed from DB
|
* MySQL and SQLite supported as database backends
|
||||||
* Event log: Track what changes happen to your inventory, track which user does what. Revert your parts to older
|
|
||||||
versions.
|
|
||||||
* Responsive design: You can use Part-DB on your PC, your tablet, and your smartphone using the same interface.
|
|
||||||
* MySQL, SQLite and PostgreSQL are supported as database backends
|
|
||||||
* Support for rich text descriptions and comments in parts
|
* Support for rich text descriptions and comments in parts
|
||||||
* Support for multiple currencies and automatic update of exchange rates supported
|
* Support for multiple currencies and automatic update of exchange rates supported
|
||||||
* Powerful search and filter function, including parametric search (search for parts according to some specifications)
|
* Powerful search and filter function, including parametric search (search for parts according to some specifications)
|
||||||
* Automatic thumbnail generation for pictures
|
* Automatic thumbnail generation for pictures
|
||||||
* Use cloud providers (like Octopart, Digikey, Farnell, LCSC or TME) to automatically get part information, datasheets, and
|
* Use cloud providers (like Octopart, Digikey, farnell or TME) to automatically get part information, datasheets and prices for parts
|
||||||
prices for parts
|
|
||||||
* API to access Part-DB from other applications/scripts
|
|
||||||
* [Integration with KiCad](https://docs.part-db.de/usage/eda_integration.html): Use Part-DB as the central datasource for your
|
|
||||||
KiCad and see available parts from Part-DB directly inside KiCad.
|
|
||||||
|
|
||||||
With these features, Part-DB is useful to hobbyists, who want to keep track of their private electronic parts inventory,
|
|
||||||
or maker spaces, where many users should have (controlled) access to the shared inventory.
|
With these features Part-DB is useful to hobbyists, who want to keep track of their private electronic parts inventory,
|
||||||
|
or makerspaces, where many users have should have (controlled) access to the shared inventory.
|
||||||
|
|
||||||
Part-DB is also used by small companies and universities for managing their inventory.
|
Part-DB is also used by small companies and universities for managing their inventory.
|
||||||
|
|
||||||
## Requirements
|
## Requirements
|
||||||
|
* A **web server** (like Apache2 or nginx) that is capable of running [Symfony 5](https://symfony.com/doc/current/reference/requirements.html),
|
||||||
* A **web server** (like Apache2 or nginx) that is capable of
|
this includes a minimum PHP version of **PHP 8.1**
|
||||||
running [Symfony 6](https://symfony.com/doc/current/reference/requirements.html),
|
* A **MySQL** (at least 5.7) /**MariaDB** (at least 10.2.2) database server if you do not want to use SQLite.
|
||||||
this includes a minimum PHP version of **PHP 8.1**
|
* Shell access to your server is highly suggested!
|
||||||
* A **MySQL** (at least 5.7) /**MariaDB** (at least 10.4) database server, or **PostgreSQL** 10+ if you do not want to use SQLite.
|
* For building the client side assets **yarn** and **nodejs** (>= 18.0) is needed.
|
||||||
* Shell access to your server is highly recommended!
|
|
||||||
* For building the client-side assets **yarn** and **nodejs** (>= 18.0) is needed.
|
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
|
If you want to upgrade your legacy (< 1.0.0) version of Part-DB to this version, please read [this](https://docs.part-db.de/upgrade_legacy.html) first.
|
||||||
|
|
||||||
If you want to upgrade your legacy (< 1.0.0) version of Part-DB to this version, please
|
*Hint:* A docker image is available under [jbtronics/part-db1](https://hub.docker.com/r/jbtronics/part-db1). How to set up Part-DB via docker is described [here](https://docs.part-db.de/installation/installation_docker.html).
|
||||||
read [this](https://docs.part-db.de/upgrade_legacy.html) first.
|
|
||||||
|
|
||||||
*Hint:* A docker image is available under [jbtronics/part-db1](https://hub.docker.com/r/jbtronics/part-db1). How to set
|
**Below you find some very rough outline of the installation process, see [here](https://docs.part-db.de/installation/) for a detailed guide how to install Part-DB.**
|
||||||
up Part-DB via docker is described [here](https://docs.part-db.de/installation/installation_docker.html).
|
|
||||||
|
|
||||||
**Below you find a very rough outline of the installation process, see [here](https://docs.part-db.de/installation/)
|
|
||||||
for a detailed guide on how to install Part-DB.**
|
|
||||||
|
|
||||||
1. Copy or clone this repository into a folder on your server.
|
1. Copy or clone this repository into a folder on your server.
|
||||||
2. Configure your webserver to serve from the `public/` folder.
|
2. Configure your webserver to serve from the `public/` folder. See [here](https://symfony.com/doc/current/setup/web_server_configuration.html)
|
||||||
See [here](https://symfony.com/doc/current/setup/web_server_configuration.html)
|
for additional information.
|
||||||
for additional information.
|
|
||||||
3. Copy the global config file `cp .env .env.local` and edit `.env.local`:
|
3. Copy the global config file `cp .env .env.local` and edit `.env.local`:
|
||||||
* Change the line `APP_ENV=dev` to `APP_ENV=prod`
|
* Change the line `APP_ENV=dev` to `APP_ENV=prod`
|
||||||
* If you do not want to use SQLite, change the value of `DATABASE_URL=` to your needs (
|
* If you do not want to use SQLite, change the value of `DATABASE_URL=` to your needs (see [here](http://docs.doctrine-project.org/projects/doctrine-dbal/en/latest/reference/configuration.html#connecting-using-a-url)) for the format.
|
||||||
see [here](http://docs.doctrine-project.org/projects/doctrine-dbal/en/latest/reference/configuration.html#connecting-using-a-url))
|
In bigger instances with concurrent accesses, MySQL is more performant. This can not be changed easily later, so choose wisely.
|
||||||
for the format.
|
|
||||||
In bigger instances with concurrent accesses, MySQL is more performant. This can not be changed easily later, so
|
|
||||||
choose wisely.
|
|
||||||
4. Install composer dependencies and generate autoload files: `composer install -o --no-dev`
|
4. Install composer dependencies and generate autoload files: `composer install -o --no-dev`
|
||||||
5. Install client side dependencies and build it: `yarn install` and `yarn build`
|
5. If you have put Part-DB into a sub-directory on your server (like `part-db/`), you have to edit the file
|
||||||
6. _Optional_ (speeds up first load): Warmup cache: `php bin/console cache:warmup`
|
`webpack.config.js` and uncomment the lines (remove the `//` before the lines) `.setPublicPath('/part-db/build')` (line 43) and
|
||||||
7. Upgrade database to new scheme (or create it, when it was empty): `php bin/console doctrine:migrations:migrate` and
|
`.setManifestKeyPrefix('build/')` (line 44). You have to replace `/part-db` with your own path on line 44.
|
||||||
follow the instructions given. During the process the password for the admin is user is shown. Copy it. **Caution**:
|
6. Install client side dependencies and build it: `yarn install` and `yarn build`
|
||||||
These steps tamper with your database and could potentially destroy it. So make sure to make a backup of your
|
7. _Optional_ (speeds up first load): Warmup cache: `php bin/console cache:warmup`
|
||||||
database.
|
8. Upgrade database to new scheme (or create it, when it was empty): `php bin/console doctrine:migrations:migrate` and follow the instructions given. During the process the password for the admin is user is shown. Copy it. **Caution**: This steps tamper with your database and could potentially destroy it. So make sure to make a backup of your database.
|
||||||
8. You can configure Part-DB via `config/parameters.yaml`. You should check if settings match your expectations after
|
9. You can configure Part-DB via `config/parameters.yaml`. You should check if settings match your expectations, after you installed/upgraded Part-DB. Check if `partdb.default_currency` matches your mainly used currency (this can not be changed after creating price informations).
|
||||||
you installed/upgraded Part-DB. Check if `partdb.default_currency` matches your mainly used currency (this can not be
|
Run `php bin/console cache:clear` when you changed something.
|
||||||
changed after creating price information).
|
10. Access Part-DB in your browser (under the URL you put it) and login with user *admin*. Password is the one outputted during DB setup.
|
||||||
Run `php bin/console cache:clear` when you change something.
|
If you can not remember the password, set a new one with `php bin/console app:set-password admin`. You can create new users with the admin user and start using Part-DB.
|
||||||
9. Access Part-DB in your browser (under the URL you put it) and log in with user *admin*. Password is the one outputted
|
|
||||||
during DB setup.
|
|
||||||
If you can not remember the password, set a new one with `php bin/console app:set-password admin`. You can create
|
|
||||||
new users with the admin user and start using Part-DB.
|
|
||||||
|
|
||||||
When you want to upgrade to a newer version, then just copy the new files into the folder
|
When you want to upgrade to a newer version, then just copy the new files into the folder
|
||||||
and repeat the steps 4. to 7.
|
and repeat the steps 4. to 7.
|
||||||
|
|
||||||
Normally a random password is generated when the admin user is created during initial database creation,
|
Normally a random password is generated when the admin user is created during inital database creation,
|
||||||
however, you can set the initial admin password, by setting the `INITIAL_ADMIN_PW` env var.
|
however you can set the inital admin password, by setting the `INITIAL_ADMIN_PW` env var.
|
||||||
|
|
||||||
You can configure Part-DB to your needs by changing environment variables in the `.env.local` file.
|
You can configure Part-DB to your needs by changing environment variables in the `.env.local` file.
|
||||||
See [here](https://docs.part-db.de/configuration.html) for more information.
|
See [here](https://docs.part-db.de/configuration.html) for more information.
|
||||||
|
|
||||||
### Reverse proxy
|
### Reverse proxy
|
||||||
|
If you are using a reverse proxy, you have to ensure that the proxies sets the `X-Forwarded-*` headers correctly, or you will get HTTP/HTTPS mixup and wrong hostnames.
|
||||||
If you are using a reverse proxy, you have to ensure that the proxies set the `X-Forwarded-*` headers correctly, or you
|
If the reverse proxy is on a different server (or it cannot access Part-DB via localhost) you have to set the `TRUSTED_PROXIES` env variable to match your reverse proxies IP-address (or IP block). You can do this in your `.env.local` or (when using docker) in your `docker-compose.yml` file.
|
||||||
will get HTTP/HTTPS mixup and wrong hostnames.
|
|
||||||
If the reverse proxy is on a different server (or it cannot access Part-DB via localhost) you have to set
|
|
||||||
the `TRUSTED_PROXIES` env variable to match your reverse proxy's IP address (or IP block). You can do this in
|
|
||||||
your `.env.local` or (when using docker) in your `docker-compose.yml` file.
|
|
||||||
|
|
||||||
## Donate for development
|
## Donate for development
|
||||||
|
|
||||||
If you want to donate to the Part-DB developer, see the sponsor button in the top bar (next to the repo name).
|
If you want to donate to the Part-DB developer, see the sponsor button in the top bar (next to the repo name).
|
||||||
There you will find various methods to support development on a monthly or a one-time base.
|
There you will find various methods to support development on a monthly or a one time base.
|
||||||
|
|
||||||
## Built with
|
## Built with
|
||||||
|
|
||||||
* [Symfony 5](https://symfony.com/): The main framework used for the serverside PHP
|
* [Symfony 5](https://symfony.com/): The main framework used for the serverside PHP
|
||||||
* [Bootstrap 5](https://getbootstrap.com/) and [Bootswatch](https://bootswatch.com/): Used as website theme
|
* [Bootstrap 5](https://getbootstrap.com/) and [Bootswatch](https://bootswatch.com/): Used as website theme
|
||||||
* [Fontawesome](https://fontawesome.com/): Used as icon set
|
* [Fontawesome](https://fontawesome.com/): Used as icon set
|
||||||
* [Hotwire Stimulus](https://stimulus.hotwired.dev/) and [Hotwire Turbo](https://turbo.hotwired.dev/): Frontend
|
* [Hotwire Stimulus](https://stimulus.hotwired.dev/) and [Hotwire Turbo](https://turbo.hotwired.dev/): Frontend Javascript
|
||||||
Javascript
|
|
||||||
|
|
||||||
## Authors
|
## Authors
|
||||||
|
* **Jan Böhmer** - *Inital work* - [Github](https://github.com/jbtronics/)
|
||||||
|
|
||||||
* **Jan Böhmer** - *Initial work* - [GitHub](https://github.com/jbtronics/)
|
See also the list of [contributors](https://github.com/Part-DB/Part-DB-server/graphs/contributors) who participated in this project.
|
||||||
|
|
||||||
See also the list of [contributors](https://github.com/Part-DB/Part-DB-server/graphs/contributors) who participated in
|
|
||||||
this project.
|
|
||||||
|
|
||||||
Based on the original Part-DB by Christoph Lechner and K. Jacobs
|
Based on the original Part-DB by Christoph Lechner and K. Jacobs
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
Part-DB is licensed under the GNU Affero General Public License v3.0 (or at your opinion any later).
|
Part-DB is licensed under the GNU Affero General Public License v3.0 (or at your opinion any later).
|
||||||
This mostly means that you can use Part-DB for whatever you want (even use it commercially)
|
This mostly means that you can use Part-DB for whatever you want (even use it commercially)
|
||||||
as long as you publish the source code for every change you make under the AGPL, too.
|
as long as you publish the source code for every change you make under the AGPL, too.
|
||||||
|
|
2
VERSION
2
VERSION
|
@ -1 +1 @@
|
||||||
1.17.1
|
1.7.3
|
||||||
|
|
|
@ -85,9 +85,6 @@ const PLACEHOLDERS = [
|
||||||
['[[COMMENT_T]]', 'Comment (plain text)'],
|
['[[COMMENT_T]]', 'Comment (plain text)'],
|
||||||
['[[LAST_MODIFIED]]', 'Last modified datetime'],
|
['[[LAST_MODIFIED]]', 'Last modified datetime'],
|
||||||
['[[CREATION_DATE]]', 'Creation datetime'],
|
['[[CREATION_DATE]]', 'Creation datetime'],
|
||||||
['[[IPN_BARCODE_QR]]', 'IPN as QR code'],
|
|
||||||
['[[IPN_BARCODE_C128]]', 'IPN as Code 128 barcode'],
|
|
||||||
['[[IPN_BARCODE_C39]]', 'IPN as Code 39 barcode'],
|
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -128,8 +125,6 @@ const PLACEHOLDERS = [
|
||||||
['[[BARCODE_QR]]', 'QR code linking to this element'],
|
['[[BARCODE_QR]]', 'QR code linking to this element'],
|
||||||
['[[BARCODE_C128]]', 'Code 128 barcode linking to this element'],
|
['[[BARCODE_C128]]', 'Code 128 barcode linking to this element'],
|
||||||
['[[BARCODE_C39]]', 'Code 39 barcode linking to this element'],
|
['[[BARCODE_C39]]', 'Code 39 barcode linking to this element'],
|
||||||
['[[BARCODE_C93]]', 'Code 93 barcode linking to this element'],
|
|
||||||
['[[BARCODE_DATAMATRIX]]', 'Datamatrix code linking to this element'],
|
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
|
@ -48,9 +48,6 @@ Object.assign( window.CKEDITOR_TRANSLATIONS[ 'de' ].dictionary, {
|
||||||
'Comment (plain text)': 'Kommentar (Nur-Text)',
|
'Comment (plain text)': 'Kommentar (Nur-Text)',
|
||||||
'Last modified datetime': 'Zuletzt geändert',
|
'Last modified datetime': 'Zuletzt geändert',
|
||||||
'Creation datetime': 'Erstellt',
|
'Creation datetime': 'Erstellt',
|
||||||
'IPN as QR code': 'IPN als QR Code',
|
|
||||||
'IPN as Code 128 barcode': 'IPN als Code 128 Barcode',
|
|
||||||
'IPN as Code 39 barcode': 'IPN als Code 39 Barcode',
|
|
||||||
|
|
||||||
'Lot ID': 'Lot ID',
|
'Lot ID': 'Lot ID',
|
||||||
'Lot name': 'Lot Name',
|
'Lot name': 'Lot Name',
|
||||||
|
@ -69,8 +66,6 @@ Object.assign( window.CKEDITOR_TRANSLATIONS[ 'de' ].dictionary, {
|
||||||
'QR code linking to this element': 'QR Code verknüpft mit diesem Element',
|
'QR code linking to this element': 'QR Code verknüpft mit diesem Element',
|
||||||
'Code 128 barcode linking to this element': 'Code 128 Barcode verknüpft mit diesem Element',
|
'Code 128 barcode linking to this element': 'Code 128 Barcode verknüpft mit diesem Element',
|
||||||
'Code 39 barcode linking to this element': 'Code 39 Barcode verknüpft mit diesem Element',
|
'Code 39 barcode linking to this element': 'Code 39 Barcode verknüpft mit diesem Element',
|
||||||
'Code 93 barcode linking to this element': 'Code 93 Barcode verknüpft mit diesem Element',
|
|
||||||
'Datamatrix code linking to this element': 'Datamatrix Code verknüpft mit diesem Element',
|
|
||||||
|
|
||||||
'Location ID': 'Lagerort ID',
|
'Location ID': 'Lagerort ID',
|
||||||
'Name': 'Name',
|
'Name': 'Name',
|
||||||
|
|
|
@ -88,8 +88,5 @@ export default class extends Controller {
|
||||||
} else {
|
} else {
|
||||||
this.hideSidebar();
|
this.hideSidebar();
|
||||||
}
|
}
|
||||||
|
|
||||||
//Hide the tootip on the button
|
|
||||||
this._toggle_button.blur();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -20,26 +20,18 @@
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
import { Controller } from '@hotwired/stimulus';
|
import { Controller } from '@hotwired/stimulus';
|
||||||
import { Marked } from "marked";
|
import { marked } from "marked";
|
||||||
import { mangle } from "marked-mangle";
|
import { mangle } from "marked-mangle";
|
||||||
import { gfmHeadingId } from "marked-gfm-heading-id";
|
import { gfmHeadingId } from "marked-gfm-heading-id";
|
||||||
import DOMPurify from 'dompurify';
|
import DOMPurify from 'dompurify';
|
||||||
|
|
||||||
import "../../css/app/markdown.css";
|
import "../../css/app/markdown.css";
|
||||||
|
|
||||||
export default class MarkdownController extends Controller {
|
export default class extends Controller {
|
||||||
|
|
||||||
static _marked = new Marked([
|
|
||||||
{
|
|
||||||
gfm: true,
|
|
||||||
},
|
|
||||||
gfmHeadingId(),
|
|
||||||
mangle(),
|
|
||||||
])
|
|
||||||
;
|
|
||||||
|
|
||||||
connect()
|
connect()
|
||||||
{
|
{
|
||||||
|
this.configureMarked();
|
||||||
this.render();
|
this.render();
|
||||||
|
|
||||||
//Dispatch an event that we are now finished
|
//Dispatch an event that we are now finished
|
||||||
|
@ -53,7 +45,7 @@ export default class MarkdownController extends Controller {
|
||||||
let raw = this.element.dataset['markdown'];
|
let raw = this.element.dataset['markdown'];
|
||||||
|
|
||||||
//Apply purified parsed markdown
|
//Apply purified parsed markdown
|
||||||
this.element.innerHTML = DOMPurify.sanitize(MarkdownController._marked.parse(this.unescapeHTML(raw)));
|
this.element.innerHTML = DOMPurify.sanitize(marked(this.unescapeHTML(raw)));
|
||||||
|
|
||||||
for(let a of this.element.querySelectorAll('a')) {
|
for(let a of this.element.querySelectorAll('a')) {
|
||||||
//Mark all links as external
|
//Mark all links as external
|
||||||
|
@ -89,17 +81,8 @@ export default class MarkdownController extends Controller {
|
||||||
/**
|
/**
|
||||||
* Configure the marked parser
|
* Configure the marked parser
|
||||||
*/
|
*/
|
||||||
/*static newMarked()
|
configureMarked()
|
||||||
{
|
{
|
||||||
const marked = new Marked([
|
|
||||||
{
|
|
||||||
gfm: true,
|
|
||||||
},
|
|
||||||
gfmHeadingId(),
|
|
||||||
mangle(),
|
|
||||||
])
|
|
||||||
;
|
|
||||||
|
|
||||||
marked.use(mangle());
|
marked.use(mangle());
|
||||||
marked.use(gfmHeadingId({
|
marked.use(gfmHeadingId({
|
||||||
}));
|
}));
|
||||||
|
@ -107,5 +90,5 @@ export default class MarkdownController extends Controller {
|
||||||
marked.setOptions({
|
marked.setOptions({
|
||||||
gfm: true,
|
gfm: true,
|
||||||
});
|
});
|
||||||
}*/
|
}
|
||||||
}
|
}
|
|
@ -23,12 +23,6 @@ import "tom-select/dist/css/tom-select.bootstrap5.css";
|
||||||
import '../../css/components/tom-select_extensions.css';
|
import '../../css/components/tom-select_extensions.css';
|
||||||
import TomSelect from "tom-select";
|
import TomSelect from "tom-select";
|
||||||
|
|
||||||
import TomSelect_click_to_edit from '../../tomselect/click_to_edit/click_to_edit'
|
|
||||||
import TomSelect_autoselect_typed from '../../tomselect/autoselect_typed/autoselect_typed'
|
|
||||||
|
|
||||||
TomSelect.define('click_to_edit', TomSelect_click_to_edit)
|
|
||||||
TomSelect.define('autoselect_typed', TomSelect_autoselect_typed)
|
|
||||||
|
|
||||||
export default class extends Controller {
|
export default class extends Controller {
|
||||||
_tomSelect;
|
_tomSelect;
|
||||||
|
|
||||||
|
@ -52,12 +46,6 @@ export default class extends Controller {
|
||||||
}
|
}
|
||||||
return '<div>' + escape(data.label) + '</div>';
|
return '<div>' + escape(data.label) + '</div>';
|
||||||
}
|
}
|
||||||
},
|
|
||||||
plugins: {
|
|
||||||
'autoselect_typed': {},
|
|
||||||
'click_to_edit': {},
|
|
||||||
'clear_button': {},
|
|
||||||
"restore_on_backspace": {}
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -53,7 +53,6 @@ export default class extends Controller {
|
||||||
|
|
||||||
const config = {
|
const config = {
|
||||||
language: language,
|
language: language,
|
||||||
licenseKey: "GPL",
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const watchdog = new EditorWatchdog();
|
const watchdog = new EditorWatchdog();
|
||||||
|
@ -71,9 +70,7 @@ export default class extends Controller {
|
||||||
editor_div.classList.add(...new_classes.split(","));
|
editor_div.classList.add(...new_classes.split(","));
|
||||||
}
|
}
|
||||||
|
|
||||||
//This return is important! Otherwise we get mysterious errors in the console
|
console.log(editor);
|
||||||
//See: https://github.com/ckeditor/ckeditor5/issues/5897#issuecomment-628471302
|
|
||||||
return editor;
|
|
||||||
})
|
})
|
||||||
.catch(error => {
|
.catch(error => {
|
||||||
console.error(error);
|
console.error(error);
|
||||||
|
|
|
@ -75,49 +75,13 @@ export default class extends Controller {
|
||||||
|
|
||||||
//Insert new html after the last child element
|
//Insert new html after the last child element
|
||||||
//If the table has a tbody, insert it there
|
//If the table has a tbody, insert it there
|
||||||
//Afterwards return the newly created row
|
|
||||||
if(targetTable.tBodies[0]) {
|
if(targetTable.tBodies[0]) {
|
||||||
targetTable.tBodies[0].insertAdjacentHTML('beforeend', newElementStr);
|
targetTable.tBodies[0].insertAdjacentHTML('beforeend', newElementStr);
|
||||||
return targetTable.tBodies[0].lastElementChild;
|
|
||||||
} else { //Otherwise just insert it
|
} else { //Otherwise just insert it
|
||||||
targetTable.insertAdjacentHTML('beforeend', newElementStr);
|
targetTable.insertAdjacentHTML('beforeend', newElementStr);
|
||||||
return targetTable.lastElementChild;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* This action opens a file dialog to select multiple files and then creates a new element for each file, where
|
|
||||||
* the file is assigned to the input field.
|
|
||||||
* This should only be used for attachments collection types
|
|
||||||
* @param event
|
|
||||||
*/
|
|
||||||
uploadMultipleFiles(event) {
|
|
||||||
//Open a file dialog to select multiple files
|
|
||||||
const input = document.createElement('input');
|
|
||||||
input.type = 'file';
|
|
||||||
input.multiple = true;
|
|
||||||
input.click();
|
|
||||||
|
|
||||||
input.addEventListener('change', (event) => {
|
|
||||||
//Create a element for each file
|
|
||||||
|
|
||||||
for (let i = 0; i < input.files.length; i++) {
|
|
||||||
const file = input.files[i];
|
|
||||||
|
|
||||||
const newElement = this.createElement(event);
|
|
||||||
const rowInput = newElement.querySelector("input[type='file']");
|
|
||||||
|
|
||||||
//We can not directly assign the file to the input, so we have to create a new DataTransfer object
|
|
||||||
const dataTransfer = new DataTransfer();
|
|
||||||
dataTransfer.items.add(file);
|
|
||||||
|
|
||||||
rowInput.files = dataTransfer.files;
|
|
||||||
}
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Similar to createEvent Pricedetails need some special handling to fill min amount
|
* Similar to createEvent Pricedetails need some special handling to fill min amount
|
||||||
* @param event
|
* @param event
|
||||||
|
|
|
@ -24,25 +24,18 @@ import 'datatables.net-bs5/css/dataTables.bootstrap5.css'
|
||||||
import 'datatables.net-buttons-bs5/css/buttons.bootstrap5.css'
|
import 'datatables.net-buttons-bs5/css/buttons.bootstrap5.css'
|
||||||
import 'datatables.net-fixedheader-bs5/css/fixedHeader.bootstrap5.css'
|
import 'datatables.net-fixedheader-bs5/css/fixedHeader.bootstrap5.css'
|
||||||
import 'datatables.net-responsive-bs5/css/responsive.bootstrap5.css';
|
import 'datatables.net-responsive-bs5/css/responsive.bootstrap5.css';
|
||||||
|
import 'datatables.net-select-bs5/css/select.bootstrap5.css';
|
||||||
//Use our own styles for the select extension which fit the bootstrap theme better
|
|
||||||
//import 'datatables.net-select-bs5/css/select.bootstrap5.css';
|
|
||||||
import '../../../css/components/datatables_select_bs5.css';
|
|
||||||
|
|
||||||
//JS
|
//JS
|
||||||
import 'datatables.net-bs5';
|
import 'datatables.net-bs5';
|
||||||
import 'datatables.net-buttons-bs5';
|
import 'datatables.net-buttons-bs5';
|
||||||
import 'datatables.net-buttons/js/buttons.colVis.js';
|
import 'datatables.net-buttons/js/buttons.colVis.js';
|
||||||
import 'datatables.net-fixedheader-bs5';
|
import 'datatables.net-fixedheader-bs5';
|
||||||
|
import 'datatables.net-select-bs5';
|
||||||
import 'datatables.net-colreorder-bs5';
|
import 'datatables.net-colreorder-bs5';
|
||||||
import 'datatables.net-responsive-bs5';
|
import 'datatables.net-responsive-bs5';
|
||||||
import '../../../js/lib/datatables';
|
import '../../../js/lib/datatables';
|
||||||
|
|
||||||
//import 'datatables.net-select-bs5';
|
|
||||||
//Use the local version containing the fix for the select extension
|
|
||||||
import '../../../js/lib/dataTables.select.mjs';
|
|
||||||
|
|
||||||
|
|
||||||
const EVENT_DT_LOADED = 'dt:loaded';
|
const EVENT_DT_LOADED = 'dt:loaded';
|
||||||
|
|
||||||
export default class extends Controller {
|
export default class extends Controller {
|
||||||
|
@ -139,7 +132,7 @@ export default class extends Controller {
|
||||||
if(this.isSelectable()) {
|
if(this.isSelectable()) {
|
||||||
options.select = {
|
options.select = {
|
||||||
style: 'multi+shift',
|
style: 'multi+shift',
|
||||||
selector: 'td.dt-select',
|
selector: 'td.select-checkbox'
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -152,12 +145,6 @@ export default class extends Controller {
|
||||||
|
|
||||||
//Fix height of the length selector
|
//Fix height of the length selector
|
||||||
promise.then((dt) => {
|
promise.then((dt) => {
|
||||||
|
|
||||||
//Draw the rows to make sure the correct status text is displayed ("No matching records found" instead of "Loading...")
|
|
||||||
if (dt.data().length === 0) {
|
|
||||||
dt.rows().draw()
|
|
||||||
}
|
|
||||||
|
|
||||||
//Find all length selectors (select with name dt_length), which are inside a label
|
//Find all length selectors (select with name dt_length), which are inside a label
|
||||||
const lengthSelectors = document.querySelectorAll('label select[name="dt_length"]');
|
const lengthSelectors = document.querySelectorAll('label select[name="dt_length"]');
|
||||||
//And remove the surrounding label, while keeping the select with all event handlers
|
//And remove the surrounding label, while keeping the select with all event handlers
|
||||||
|
@ -193,6 +180,27 @@ export default class extends Controller {
|
||||||
dt.fixedHeader.headerOffset($("#navbar").outerHeight());
|
dt.fixedHeader.headerOffset($("#navbar").outerHeight());
|
||||||
});
|
});
|
||||||
|
|
||||||
|
//Register event handler to selectAllRows checkbox if available
|
||||||
|
if (this.isSelectable()) {
|
||||||
|
promise.then((dt) => {
|
||||||
|
const selectAllCheckbox = this.element.querySelector('thead th.select-checkbox');
|
||||||
|
selectAllCheckbox.addEventListener('click', () => {
|
||||||
|
if(selectAllCheckbox.parentElement.classList.contains('selected')) {
|
||||||
|
dt.rows().deselect();
|
||||||
|
selectAllCheckbox.parentElement.classList.remove('selected');
|
||||||
|
} else {
|
||||||
|
dt.rows().select();
|
||||||
|
selectAllCheckbox.parentElement.classList.add('selected');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
//When any row is deselected, also deselect the selectAll checkbox
|
||||||
|
dt.on('deselect.dt', () => {
|
||||||
|
selectAllCheckbox.parentElement.classList.remove('selected');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
//Allow to further configure the datatable
|
//Allow to further configure the datatable
|
||||||
promise.then(this._afterLoaded.bind(this));
|
promise.then(this._afterLoaded.bind(this));
|
||||||
|
|
||||||
|
|
|
@ -43,8 +43,7 @@ export default class extends Controller
|
||||||
const message = this.element.dataset.deleteMessage;
|
const message = this.element.dataset.deleteMessage;
|
||||||
const title = this.element.dataset.deleteTitle;
|
const title = this.element.dataset.deleteTitle;
|
||||||
|
|
||||||
//Use event target, to find the form, where the submit button was clicked
|
const form = this.element;
|
||||||
const form = event.target;
|
|
||||||
const submitter = event.submitter;
|
const submitter = event.submitter;
|
||||||
const that = this;
|
const that = this;
|
||||||
|
|
||||||
|
|
|
@ -1,67 +0,0 @@
|
||||||
/*
|
|
||||||
* This file is part of Part-DB (https://github.com/Part-DB/Part-DB-symfony).
|
|
||||||
*
|
|
||||||
* Copyright (C) 2019 - 2023 Jan Böhmer (https://github.com/jbtronics)
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU Affero General Public License as published
|
|
||||||
* by the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU Affero General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Affero General Public License
|
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
import {Controller} from "@hotwired/stimulus";
|
|
||||||
|
|
||||||
export default class extends Controller
|
|
||||||
{
|
|
||||||
static values = {
|
|
||||||
id: String
|
|
||||||
}
|
|
||||||
|
|
||||||
connect() {
|
|
||||||
this.loadState()
|
|
||||||
this.element.addEventListener('change', () => {
|
|
||||||
this.saveState()
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
loadState() {
|
|
||||||
let storageKey = this.getStorageKey();
|
|
||||||
let value = localStorage.getItem(storageKey);
|
|
||||||
if (value === null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (value === 'true') {
|
|
||||||
this.element.checked = true
|
|
||||||
}
|
|
||||||
if (value === 'false') {
|
|
||||||
this.element.checked = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
saveState() {
|
|
||||||
let storageKey = this.getStorageKey();
|
|
||||||
|
|
||||||
if (this.element.checked) {
|
|
||||||
localStorage.setItem(storageKey, 'true');
|
|
||||||
} else {
|
|
||||||
localStorage.setItem(storageKey, 'false');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
getStorageKey() {
|
|
||||||
if (this.hasIdValue) {
|
|
||||||
return 'persistent_checkbox_' + this.idValue
|
|
||||||
}
|
|
||||||
|
|
||||||
return 'persistent_checkbox_' + this.element.id;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,200 +0,0 @@
|
||||||
/*
|
|
||||||
* This file is part of Part-DB (https://github.com/Part-DB/Part-DB-symfony).
|
|
||||||
*
|
|
||||||
* Copyright (C) 2019 - 2024 Jan Böhmer (https://github.com/jbtronics)
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU Affero General Public License as published
|
|
||||||
* by the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU Affero General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Affero General Public License
|
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
import { Controller } from "@hotwired/stimulus";
|
|
||||||
import { autocomplete } from '@algolia/autocomplete-js';
|
|
||||||
//import "@algolia/autocomplete-theme-classic/dist/theme.css";
|
|
||||||
import "../../css/components/autocomplete_bootstrap_theme.css";
|
|
||||||
import { createLocalStorageRecentSearchesPlugin } from '@algolia/autocomplete-plugin-recent-searches';
|
|
||||||
import {marked} from "marked";
|
|
||||||
|
|
||||||
import {
|
|
||||||
trans,
|
|
||||||
SEARCH_PLACEHOLDER,
|
|
||||||
SEARCH_SUBMIT,
|
|
||||||
STATISTICS_PARTS
|
|
||||||
} from '../../translator';
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This controller is responsible for the search fields in the navbar and the homepage.
|
|
||||||
* It uses the Algolia Autocomplete library to provide a fast and responsive search.
|
|
||||||
*/
|
|
||||||
export default class extends Controller {
|
|
||||||
|
|
||||||
static targets = ["input"];
|
|
||||||
|
|
||||||
_autocomplete;
|
|
||||||
|
|
||||||
// Highlight the search query in the results
|
|
||||||
_highlight = (text, query) => {
|
|
||||||
if (!text) return text;
|
|
||||||
if (!query) return text;
|
|
||||||
|
|
||||||
const HIGHLIGHT_PRE_TAG = '__aa-highlight__'
|
|
||||||
const HIGHLIGHT_POST_TAG = '__/aa-highlight__'
|
|
||||||
|
|
||||||
const escaped = query.replace(/[-/\\^$*+?.()|[\]{}]/g, '\\$&');
|
|
||||||
const regex = new RegExp(escaped, 'gi');
|
|
||||||
|
|
||||||
return text.replace(regex, (match) => `${HIGHLIGHT_PRE_TAG}${match}${HIGHLIGHT_POST_TAG}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
initialize() {
|
|
||||||
// The endpoint for searching parts
|
|
||||||
const base_url = this.element.dataset.autocomplete;
|
|
||||||
// The URL template for the part detail pages
|
|
||||||
const part_detail_uri_template = this.element.dataset.detailUrl;
|
|
||||||
|
|
||||||
//The URL of the placeholder picture
|
|
||||||
const placeholder_image = this.element.dataset.placeholderImage;
|
|
||||||
|
|
||||||
//If the element is in navbar mode, or not
|
|
||||||
const navbar_mode = this.element.dataset.navbarMode === "true";
|
|
||||||
|
|
||||||
const that = this;
|
|
||||||
|
|
||||||
const recentSearchesPlugin = createLocalStorageRecentSearchesPlugin({
|
|
||||||
key: 'RECENT_SEARCH',
|
|
||||||
limit: 5,
|
|
||||||
});
|
|
||||||
|
|
||||||
this._autocomplete = autocomplete({
|
|
||||||
container: this.element,
|
|
||||||
//Place the panel in the navbar, if the element is in navbar mode
|
|
||||||
panelContainer: navbar_mode ? document.getElementById("navbar-search-form") : document.body,
|
|
||||||
panelPlacement: this.element.dataset.panelPlacement,
|
|
||||||
plugins: [recentSearchesPlugin],
|
|
||||||
openOnFocus: true,
|
|
||||||
placeholder: trans(SEARCH_PLACEHOLDER),
|
|
||||||
translations: {
|
|
||||||
submitButtonTitle: trans(SEARCH_SUBMIT)
|
|
||||||
},
|
|
||||||
|
|
||||||
// Use a navigator compatible with turbo:
|
|
||||||
navigator: {
|
|
||||||
navigate({ itemUrl }) {
|
|
||||||
window.Turbo.visit(itemUrl, { action: "advance" });
|
|
||||||
},
|
|
||||||
navigateNewTab({ itemUrl }) {
|
|
||||||
const windowReference = window.open(itemUrl, '_blank', 'noopener');
|
|
||||||
|
|
||||||
if (windowReference) {
|
|
||||||
windowReference.focus();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
navigateNewWindow({ itemUrl }) {
|
|
||||||
window.open(itemUrl, '_blank', 'noopener');
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
// If the form is submitted, forward the term to the form
|
|
||||||
onSubmit({state, event, ...setters}) {
|
|
||||||
//Put the current text into each target input field
|
|
||||||
const input = that.inputTarget;
|
|
||||||
|
|
||||||
if (!input) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
//Do not submit the form, if the input is empty
|
|
||||||
if (state.query === "") {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
input.value = state.query;
|
|
||||||
input.form.requestSubmit();
|
|
||||||
},
|
|
||||||
|
|
||||||
|
|
||||||
getSources({ query }) {
|
|
||||||
return [
|
|
||||||
// The parts source
|
|
||||||
{
|
|
||||||
sourceId: 'parts',
|
|
||||||
getItems() {
|
|
||||||
const url = base_url.replace('__QUERY__', encodeURIComponent(query));
|
|
||||||
|
|
||||||
const data = fetch(url)
|
|
||||||
.then((response) => response.json())
|
|
||||||
;
|
|
||||||
|
|
||||||
//Iterate over all fields besides the id and highlight them
|
|
||||||
const fields = ["name", "description", "category", "footprint"];
|
|
||||||
|
|
||||||
data.then((items) => {
|
|
||||||
items.forEach((item) => {
|
|
||||||
for (const field of fields) {
|
|
||||||
item[field] = that._highlight(item[field], query);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
return data;
|
|
||||||
},
|
|
||||||
getItemUrl({ item }) {
|
|
||||||
return part_detail_uri_template.replace('__ID__', item.id);
|
|
||||||
},
|
|
||||||
templates: {
|
|
||||||
header({ html }) {
|
|
||||||
return html`<span class="aa-SourceHeaderTitle">${trans(STATISTICS_PARTS)}</span>
|
|
||||||
<div class="aa-SourceHeaderLine" />`;
|
|
||||||
},
|
|
||||||
item({item, components, html}) {
|
|
||||||
const details_url = part_detail_uri_template.replace('__ID__', item.id);
|
|
||||||
|
|
||||||
return html`
|
|
||||||
<a class="aa-ItemLink" href="${details_url}">
|
|
||||||
<div class="aa-ItemContent">
|
|
||||||
<div class="aa-ItemIcon aa-ItemIcon--picture aa-ItemIcon--alignTop">
|
|
||||||
<img src="${item.image !== "" ? item.image : placeholder_image}" alt="${item.name}" width="30" height="30"/>
|
|
||||||
</div>
|
|
||||||
<div class="aa-ItemContentBody">
|
|
||||||
<div class="aa-ItemContentTitle">
|
|
||||||
<b>
|
|
||||||
${components.Highlight({hit: item, attribute: 'name'})}
|
|
||||||
</b>
|
|
||||||
</div>
|
|
||||||
<div class="aa-ItemContentDescription">
|
|
||||||
${components.Highlight({hit: item, attribute: 'description'})}
|
|
||||||
${item.category ? html`<p class="m-0"><span class="fa-solid fa-tags fa-fw"></span>${components.Highlight({hit: item, attribute: 'category'})}</p>` : ""}
|
|
||||||
${item.footprint ? html`<p class="m-0"><span class="fa-solid fa-microchip fa-fw"></span>${components.Highlight({hit: item, attribute: 'footprint'})}</p>` : ""}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</a>
|
|
||||||
`;
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
];
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
//Try to find the input field and register a defocus handler. This is necessarry, as by default the autocomplete
|
|
||||||
//lib has problems when multiple inputs are present on the page. (see https://github.com/algolia/autocomplete/issues/1216)
|
|
||||||
const inputs = this.element.getElementsByClassName('aa-Input');
|
|
||||||
for (const input of inputs) {
|
|
||||||
input.addEventListener('blur', () => {
|
|
||||||
this._autocomplete.setIsOpen(false);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -27,7 +27,7 @@ export default class extends Controller {
|
||||||
}
|
}
|
||||||
|
|
||||||
let tmp = '<div class="row m-0">' +
|
let tmp = '<div class="row m-0">' +
|
||||||
"<div class='col-2 p-0 d-flex align-items-center' style='max-width: 80px;'>" +
|
"<div class='col-2 p-0 d-flex align-items-center'>" +
|
||||||
(data.image ? "<img class='typeahead-image' src='" + data.image + "'/>" : "") +
|
(data.image ? "<img class='typeahead-image' src='" + data.image + "'/>" : "") +
|
||||||
"</div>" +
|
"</div>" +
|
||||||
"<div class='col-10'>" +
|
"<div class='col-10'>" +
|
||||||
|
|
|
@ -1,106 +0,0 @@
|
||||||
/*
|
|
||||||
* This file is part of Part-DB (https://github.com/Part-DB/Part-DB-symfony).
|
|
||||||
*
|
|
||||||
* Copyright (C) 2019 - 2023 Jan Böhmer (https://github.com/jbtronics)
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU Affero General Public License as published
|
|
||||||
* by the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU Affero General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Affero General Public License
|
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
import {Controller} from "@hotwired/stimulus";
|
|
||||||
|
|
||||||
import "tom-select/dist/css/tom-select.bootstrap5.css";
|
|
||||||
import '../../css/components/tom-select_extensions.css';
|
|
||||||
import TomSelect from "tom-select";
|
|
||||||
|
|
||||||
import TomSelect_click_to_edit from '../../tomselect/click_to_edit/click_to_edit'
|
|
||||||
import TomSelect_autoselect_typed from '../../tomselect/autoselect_typed/autoselect_typed'
|
|
||||||
|
|
||||||
TomSelect.define('click_to_edit', TomSelect_click_to_edit)
|
|
||||||
TomSelect.define('autoselect_typed', TomSelect_autoselect_typed)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This is the frontend controller for StaticFileAutocompleteType form element.
|
|
||||||
* Basically it loads a text file from the given url (via data-url) and uses it as a source for the autocomplete.
|
|
||||||
* The file is just a list of strings, one per line, which will be used as the autocomplete options.
|
|
||||||
* Lines starting with # will be ignored.
|
|
||||||
*/
|
|
||||||
export default class extends Controller {
|
|
||||||
_tomSelect;
|
|
||||||
|
|
||||||
connect() {
|
|
||||||
|
|
||||||
let settings = {
|
|
||||||
persistent: false,
|
|
||||||
create: true,
|
|
||||||
maxItems: 1,
|
|
||||||
maxOptions: 100,
|
|
||||||
createOnBlur: true,
|
|
||||||
selectOnTab: true,
|
|
||||||
valueField: 'text',
|
|
||||||
searchField: 'text',
|
|
||||||
orderField: 'text',
|
|
||||||
|
|
||||||
//This a an ugly solution to disable the delimiter parsing of the TomSelect plugin
|
|
||||||
delimiter: 'VERY_L0NG_D€LIMITER_WHICH_WILL_NEVER_BE_ENCOUNTERED_IN_A_STRING',
|
|
||||||
plugins: {
|
|
||||||
'autoselect_typed': {},
|
|
||||||
'click_to_edit': {},
|
|
||||||
'clear_button': {},
|
|
||||||
'restore_on_backspace': {}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
if (this.element.dataset.url) {
|
|
||||||
const url = this.element.dataset.url;
|
|
||||||
settings.load = (query, callback) => {
|
|
||||||
const self = this;
|
|
||||||
if (self.loading > 1) {
|
|
||||||
callback();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
fetch(url)
|
|
||||||
.then(response => response.text())
|
|
||||||
.then(text => {
|
|
||||||
// Convert the text file to array
|
|
||||||
let lines = text.split("\n");
|
|
||||||
//Remove all lines beginning with #
|
|
||||||
lines = lines.filter(x => !x.startsWith("#"));
|
|
||||||
|
|
||||||
//Convert the array to an object, where each line is in the text field
|
|
||||||
lines = lines.map(x => {
|
|
||||||
return {text: x};
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
//Unset the load function to prevent endless recursion
|
|
||||||
self._tomSelect.settings.load = null;
|
|
||||||
|
|
||||||
callback(lines);
|
|
||||||
}).catch(() => {
|
|
||||||
callback();
|
|
||||||
});
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
this._tomSelect = new TomSelect(this.element, settings);
|
|
||||||
}
|
|
||||||
|
|
||||||
disconnect() {
|
|
||||||
super.disconnect();
|
|
||||||
//Destroy the TomSelect instance
|
|
||||||
this._tomSelect.destroy();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -24,8 +24,6 @@ import {Controller} from "@hotwired/stimulus";
|
||||||
|
|
||||||
import {trans, ENTITY_SELECT_GROUP_NEW_NOT_ADDED_TO_DB} from '../../translator.js'
|
import {trans, ENTITY_SELECT_GROUP_NEW_NOT_ADDED_TO_DB} from '../../translator.js'
|
||||||
|
|
||||||
import TomSelect_autoselect_typed from '../../tomselect/autoselect_typed/autoselect_typed'
|
|
||||||
TomSelect.define('autoselect_typed', TomSelect_autoselect_typed)
|
|
||||||
|
|
||||||
export default class extends Controller {
|
export default class extends Controller {
|
||||||
_tomSelect;
|
_tomSelect;
|
||||||
|
@ -40,20 +38,11 @@ export default class extends Controller {
|
||||||
const allowAdd = this.element.getAttribute("data-allow-add") === "true";
|
const allowAdd = this.element.getAttribute("data-allow-add") === "true";
|
||||||
const addHint = this.element.getAttribute("data-add-hint") ?? "";
|
const addHint = this.element.getAttribute("data-add-hint") ?? "";
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
let settings = {
|
let settings = {
|
||||||
allowEmptyOption: true,
|
allowEmptyOption: true,
|
||||||
selectOnTab: true,
|
selectOnTab: true,
|
||||||
maxOptions: null,
|
maxOptions: null,
|
||||||
create: allowAdd ? this.createItem.bind(this) : false,
|
create: allowAdd ? this.createItem.bind(this) : false,
|
||||||
createFilter: this.createFilter.bind(this),
|
|
||||||
|
|
||||||
// This three options allow us to paste element names with commas: (see issue #538)
|
|
||||||
maxItems: 1,
|
|
||||||
delimiter: "$$VERY_LONG_DELIMITER_THAT_SHOULD_NEVER_APPEAR$$",
|
|
||||||
splitOn: null,
|
|
||||||
|
|
||||||
searchField: [
|
searchField: [
|
||||||
{field: "text", weight : 2},
|
{field: "text", weight : 2},
|
||||||
|
@ -64,21 +53,7 @@ export default class extends Controller {
|
||||||
render: {
|
render: {
|
||||||
item: this.renderItem.bind(this),
|
item: this.renderItem.bind(this),
|
||||||
option: this.renderOption.bind(this),
|
option: this.renderOption.bind(this),
|
||||||
option_create: (data, escape) => {
|
option_create: function(data, escape) {
|
||||||
//If the input starts with "->", we prepend the current selected value, for easier extension of existing values
|
|
||||||
//This here handles the display part, while the createItem function handles the actual creation
|
|
||||||
if (data.input.startsWith("->")) {
|
|
||||||
//Get current selected value
|
|
||||||
const current = this._tomSelect.getItem(this._tomSelect.getValue()).textContent.replaceAll("→", "->").trim();
|
|
||||||
//Prepend it to the input
|
|
||||||
if (current) {
|
|
||||||
data.input = current + " " + data.input;
|
|
||||||
} else {
|
|
||||||
//If there is no current value, we remove the "->"
|
|
||||||
data.input = data.input.substring(2);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return '<div class="create"><i class="fa-solid fa-plus fa-fw"></i> <strong>' + escape(data.input) + '</strong>… ' +
|
return '<div class="create"><i class="fa-solid fa-plus fa-fw"></i> <strong>' + escape(data.input) + '</strong>… ' +
|
||||||
'<small class="text-muted float-end">(' + addHint +')</small>' +
|
'<small class="text-muted float-end">(' + addHint +')</small>' +
|
||||||
'</div>';
|
'</div>';
|
||||||
|
@ -88,39 +63,13 @@ export default class extends Controller {
|
||||||
//Add callbacks to update validity
|
//Add callbacks to update validity
|
||||||
onInitialize: this.updateValidity.bind(this),
|
onInitialize: this.updateValidity.bind(this),
|
||||||
onChange: this.updateValidity.bind(this),
|
onChange: this.updateValidity.bind(this),
|
||||||
|
|
||||||
plugins: {
|
|
||||||
"autoselect_typed": {},
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
//Add clear button plugin, if an empty option is present
|
|
||||||
if (this.element.querySelector("option[value='']") !== null) {
|
|
||||||
settings.plugins["clear_button"] = {};
|
|
||||||
}
|
|
||||||
|
|
||||||
this._tomSelect = new TomSelect(this.element, settings);
|
this._tomSelect = new TomSelect(this.element, settings);
|
||||||
//Do not do a sync here as this breaks the initial rendering of the empty option
|
this._tomSelect.sync();
|
||||||
//this._tomSelect.sync();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
createItem(input, callback) {
|
createItem(input, callback) {
|
||||||
|
|
||||||
//If the input starts with "->", we prepend the current selected value, for easier extension of existing values
|
|
||||||
if (input.startsWith("->")) {
|
|
||||||
//Get current selected value
|
|
||||||
let current = this._tomSelect.getItem(this._tomSelect.getValue()).textContent.replaceAll("→", "->").trim();
|
|
||||||
//Replace no break spaces with normal spaces
|
|
||||||
current = current.replaceAll("\u00A0", " ");
|
|
||||||
//Prepend it to the input
|
|
||||||
if (current) {
|
|
||||||
input = current + " " + input;
|
|
||||||
} else {
|
|
||||||
//If there is no current value, we remove the "->"
|
|
||||||
input = input.substring(2);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
callback({
|
callback({
|
||||||
//$%$ is a special value prefix, that is used to identify items, that are not yet in the DB
|
//$%$ is a special value prefix, that is used to identify items, that are not yet in the DB
|
||||||
value: '$%$' + input,
|
value: '$%$' + input,
|
||||||
|
@ -129,31 +78,6 @@ export default class extends Controller {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
createFilter(input) {
|
|
||||||
|
|
||||||
//Normalize the input (replace spacing around arrows)
|
|
||||||
if (input.includes("->")) {
|
|
||||||
const inputs = input.split("->");
|
|
||||||
inputs.forEach((value, index) => {
|
|
||||||
inputs[index] = value.trim();
|
|
||||||
});
|
|
||||||
input = inputs.join("->");
|
|
||||||
} else {
|
|
||||||
input = input.trim();
|
|
||||||
}
|
|
||||||
|
|
||||||
const options = this._tomSelect.options;
|
|
||||||
//Iterate over all options and check if the input is already present
|
|
||||||
for (let index in options) {
|
|
||||||
const option = options[index];
|
|
||||||
if (option.path === input) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
updateValidity() {
|
updateValidity() {
|
||||||
//Mark this input as invalid, if the selected option is disabled
|
//Mark this input as invalid, if the selected option is disabled
|
||||||
|
|
|
@ -23,21 +23,14 @@ import "tom-select/dist/css/tom-select.bootstrap5.css";
|
||||||
import '../../css/components/tom-select_extensions.css';
|
import '../../css/components/tom-select_extensions.css';
|
||||||
import TomSelect from "tom-select";
|
import TomSelect from "tom-select";
|
||||||
|
|
||||||
import TomSelect_click_to_edit from '../../tomselect/click_to_edit/click_to_edit'
|
|
||||||
import TomSelect_autoselect_typed from '../../tomselect/autoselect_typed/autoselect_typed'
|
|
||||||
|
|
||||||
TomSelect.define('click_to_edit', TomSelect_click_to_edit)
|
|
||||||
TomSelect.define('autoselect_typed', TomSelect_autoselect_typed)
|
|
||||||
|
|
||||||
export default class extends Controller {
|
export default class extends Controller {
|
||||||
_tomSelect;
|
_tomSelect;
|
||||||
|
|
||||||
connect() {
|
connect() {
|
||||||
let settings = {
|
let settings = {
|
||||||
plugins: {
|
plugins: {
|
||||||
remove_button:{},
|
remove_button:{
|
||||||
'autoselect_typed': {},
|
}
|
||||||
'click_to_edit': {},
|
|
||||||
},
|
},
|
||||||
persistent: false,
|
persistent: false,
|
||||||
selectOnTab: true,
|
selectOnTab: true,
|
||||||
|
|
|
@ -94,15 +94,13 @@ export default class extends Controller {
|
||||||
showTags: this._showTags,
|
showTags: this._showTags,
|
||||||
data: data,
|
data: data,
|
||||||
showIcon: true,
|
showIcon: true,
|
||||||
preventUnselect: true,
|
|
||||||
allowReselect: true,
|
|
||||||
onNodeSelected: (event) => {
|
onNodeSelected: (event) => {
|
||||||
const node = event.detail.node;
|
const node = event.detail.node;
|
||||||
if (node.href) {
|
if (node.href) {
|
||||||
window.Turbo.visit(node.href, {action: "advance"});
|
window.Turbo.visit(node.href, {action: "advance"});
|
||||||
this._registerURLWatcher(node);
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
//onNodeContextmenu: contextmenu_handler,
|
||||||
}, [BS5Theme, BS53Theme, FAIconTheme]);
|
}, [BS5Theme, BS53Theme, FAIconTheme]);
|
||||||
|
|
||||||
this.treeTarget.addEventListener(EVENT_INITIALIZED, (event) => {
|
this.treeTarget.addEventListener(EVENT_INITIALIZED, (event) => {
|
||||||
|
@ -110,42 +108,12 @@ export default class extends Controller {
|
||||||
const treeView = event.detail.treeView;
|
const treeView = event.detail.treeView;
|
||||||
treeView.revealNode(treeView.getSelected());
|
treeView.revealNode(treeView.getSelected());
|
||||||
|
|
||||||
//Add the url watcher to all selected nodes
|
|
||||||
for (const node of treeView.getSelected()) {
|
|
||||||
this._registerURLWatcher(node);
|
|
||||||
}
|
|
||||||
|
|
||||||
//Add contextmenu event listener to the tree, which allows us to open the links in a new tab with a right click
|
//Add contextmenu event listener to the tree, which allows us to open the links in a new tab with a right click
|
||||||
treeView.getTreeElement().addEventListener("contextmenu", this._onContextMenu.bind(this));
|
treeView.getTreeElement().addEventListener("contextmenu", this._onContextMenu.bind(this));
|
||||||
});
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_registerURLWatcher(node)
|
|
||||||
{
|
|
||||||
//Register a watcher for a location change, which will unselect the node, if the location changes
|
|
||||||
const desired_url = node.href;
|
|
||||||
|
|
||||||
//Ensure that the node is unselected, if the location changes
|
|
||||||
const unselectNode = () => {
|
|
||||||
//Parse url so we can properly compare them
|
|
||||||
const desired = new URL(node.href, window.location.origin);
|
|
||||||
|
|
||||||
//We only compare the pathname, because the hash and parameters should not matter
|
|
||||||
if(window.location.pathname !== desired.pathname) {
|
|
||||||
//The ignore parameter is important here, otherwise the node will not be unselected
|
|
||||||
node.setSelected(false, {silent: true, ignorePreventUnselect: true});
|
|
||||||
|
|
||||||
//Unregister the watcher
|
|
||||||
document.removeEventListener('turbo:load', unselectNode);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
//Register the watcher via hotwire turbo
|
|
||||||
//We must just load to have the new url in window.location
|
|
||||||
document.addEventListener('turbo:load', unselectNode);
|
|
||||||
}
|
|
||||||
|
|
||||||
_onContextMenu(event)
|
_onContextMenu(event)
|
||||||
{
|
{
|
||||||
//Find the node that was clicked and open link in new tab
|
//Find the node that was clicked and open link in new tab
|
||||||
|
|
|
@ -1,44 +0,0 @@
|
||||||
/*
|
|
||||||
* This file is part of Part-DB (https://github.com/Part-DB/Part-DB-symfony).
|
|
||||||
*
|
|
||||||
* Copyright (C) 2019 - 2023 Jan Böhmer (https://github.com/jbtronics)
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU Affero General Public License as published
|
|
||||||
* by the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU Affero General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Affero General Public License
|
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
import {Controller} from "@hotwired/stimulus";
|
|
||||||
|
|
||||||
export default class extends Controller {
|
|
||||||
|
|
||||||
static targets = [ "display", "select" ]
|
|
||||||
|
|
||||||
connect()
|
|
||||||
{
|
|
||||||
this.update();
|
|
||||||
this.selectTarget.addEventListener('change', this.update.bind(this));
|
|
||||||
}
|
|
||||||
|
|
||||||
update()
|
|
||||||
{
|
|
||||||
//If the select value is 0, then we show the input field
|
|
||||||
if( this.selectTarget.value === '0')
|
|
||||||
{
|
|
||||||
this.displayTarget.classList.remove('d-none');
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
this.displayTarget.classList.add('d-none');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -20,7 +20,7 @@
|
||||||
import {Controller} from "@hotwired/stimulus";
|
import {Controller} from "@hotwired/stimulus";
|
||||||
//import * as ZXing from "@zxing/library";
|
//import * as ZXing from "@zxing/library";
|
||||||
|
|
||||||
import {Html5QrcodeScanner, Html5Qrcode} from "@part-db/html5-qrcode";
|
import {Html5QrcodeScanner, Html5Qrcode} from "html5-qrcode";
|
||||||
|
|
||||||
/* stimulusFetch: 'lazy' */
|
/* stimulusFetch: 'lazy' */
|
||||||
export default class extends Controller {
|
export default class extends Controller {
|
||||||
|
@ -50,7 +50,7 @@ export default class extends Controller {
|
||||||
});
|
});
|
||||||
|
|
||||||
this._scanner = new Html5QrcodeScanner(this.element.id, {
|
this._scanner = new Html5QrcodeScanner(this.element.id, {
|
||||||
fps: 10,
|
fps: 2,
|
||||||
qrbox: qrboxFunction,
|
qrbox: qrboxFunction,
|
||||||
experimentalFeatures: {
|
experimentalFeatures: {
|
||||||
//This option improves reading quality on android chrome
|
//This option improves reading quality on android chrome
|
||||||
|
@ -61,11 +61,6 @@ export default class extends Controller {
|
||||||
this._scanner.render(this.onScanSuccess.bind(this));
|
this._scanner.render(this.onScanSuccess.bind(this));
|
||||||
}
|
}
|
||||||
|
|
||||||
disconnect() {
|
|
||||||
this._scanner.pause();
|
|
||||||
this._scanner.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
onScanSuccess(decodedText, decodedResult) {
|
onScanSuccess(decodedText, decodedResult) {
|
||||||
//Put our decoded Text into the input box
|
//Put our decoded Text into the input box
|
||||||
document.getElementById('scan_dialog_input').value = decodedText;
|
document.getElementById('scan_dialog_input').value = decodedText;
|
||||||
|
|
|
@ -25,20 +25,9 @@ import "katex/dist/katex.css";
|
||||||
export default class extends Controller {
|
export default class extends Controller {
|
||||||
static targets = ["input", "preview"];
|
static targets = ["input", "preview"];
|
||||||
|
|
||||||
static values = {
|
|
||||||
unit: {type: Boolean, default: false} //Render as upstanding (non-italic) text, useful for units
|
|
||||||
}
|
|
||||||
|
|
||||||
updatePreview()
|
updatePreview()
|
||||||
{
|
{
|
||||||
let value = "";
|
katex.render(this.inputTarget.value, this.previewTarget, {
|
||||||
if (this.unitValue) {
|
|
||||||
value = "\\mathrm{" + this.inputTarget.value + "}";
|
|
||||||
} else {
|
|
||||||
value = this.inputTarget.value;
|
|
||||||
}
|
|
||||||
|
|
||||||
katex.render(value, this.previewTarget, {
|
|
||||||
throwOnError: false,
|
throwOnError: false,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,13 +22,6 @@ import TomSelect from "tom-select";
|
||||||
import katex from "katex";
|
import katex from "katex";
|
||||||
import "katex/dist/katex.css";
|
import "katex/dist/katex.css";
|
||||||
|
|
||||||
|
|
||||||
import TomSelect_click_to_edit from '../../tomselect/click_to_edit/click_to_edit'
|
|
||||||
import TomSelect_autoselect_typed from '../../tomselect/autoselect_typed/autoselect_typed'
|
|
||||||
|
|
||||||
TomSelect.define('click_to_edit', TomSelect_click_to_edit)
|
|
||||||
TomSelect.define('autoselect_typed', TomSelect_autoselect_typed)
|
|
||||||
|
|
||||||
/* stimulusFetch: 'lazy' */
|
/* stimulusFetch: 'lazy' */
|
||||||
export default class extends Controller
|
export default class extends Controller
|
||||||
{
|
{
|
||||||
|
@ -60,10 +53,7 @@ export default class extends Controller
|
||||||
connect() {
|
connect() {
|
||||||
const settings = {
|
const settings = {
|
||||||
plugins: {
|
plugins: {
|
||||||
'autoselect_typed': {},
|
clear_button:{}
|
||||||
'click_to_edit': {},
|
|
||||||
'clear_button': {},
|
|
||||||
'restore_on_backspace': {}
|
|
||||||
},
|
},
|
||||||
persistent: false,
|
persistent: false,
|
||||||
maxItems: 1,
|
maxItems: 1,
|
||||||
|
|
|
@ -1,68 +0,0 @@
|
||||||
/*
|
|
||||||
* This file is part of Part-DB (https://github.com/Part-DB/Part-DB-symfony).
|
|
||||||
*
|
|
||||||
* Copyright (C) 2019 - 2023 Jan Böhmer (https://github.com/jbtronics)
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU Affero General Public License as published
|
|
||||||
* by the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU Affero General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Affero General Public License
|
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
import {Controller} from "@hotwired/stimulus";
|
|
||||||
|
|
||||||
export default class extends Controller
|
|
||||||
{
|
|
||||||
static targets = ['link', 'mode', 'otherSelect'];
|
|
||||||
static values = {
|
|
||||||
targetId: Number,
|
|
||||||
};
|
|
||||||
|
|
||||||
connect() {
|
|
||||||
}
|
|
||||||
|
|
||||||
update() {
|
|
||||||
const link = this.linkTarget;
|
|
||||||
const other_select = this.otherSelectTarget;
|
|
||||||
|
|
||||||
//Extract the mode using the mode radio buttons (we filter the array to get the checked one)
|
|
||||||
const mode = (this.modeTargets.filter((e)=>e.checked))[0].value;
|
|
||||||
|
|
||||||
if (other_select.value === '') {
|
|
||||||
link.classList.add('disabled');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
//Extract href template from data attribute on link target
|
|
||||||
let href = link.getAttribute('data-href-template');
|
|
||||||
|
|
||||||
let target, other;
|
|
||||||
if (mode === '1') {
|
|
||||||
target = this.targetIdValue;
|
|
||||||
other = other_select.value;
|
|
||||||
} else if (mode === '2') {
|
|
||||||
target = other_select.value;
|
|
||||||
other = this.targetIdValue;
|
|
||||||
} else {
|
|
||||||
throw 'Invalid mode';
|
|
||||||
}
|
|
||||||
|
|
||||||
//Replace placeholder with actual target id
|
|
||||||
href = href.replace('__target__', target);
|
|
||||||
//Replace placeholder with selected value of the select (the event sender)
|
|
||||||
href = href.replace('__other__', other);
|
|
||||||
|
|
||||||
//Assign new href to link
|
|
||||||
link.setAttribute('href', href);
|
|
||||||
//Make link clickable
|
|
||||||
link.classList.remove('disabled');
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -111,11 +111,4 @@ ul.structural_link li a:hover {
|
||||||
.permission-checkbox:checked {
|
.permission-checkbox:checked {
|
||||||
background-color: var(--bs-success);
|
background-color: var(--bs-success);
|
||||||
border-color: var(--bs-success);
|
border-color: var(--bs-success);
|
||||||
}
|
|
||||||
|
|
||||||
/***********************************************
|
|
||||||
* Katex rendering with same height as text
|
|
||||||
***********************************************/
|
|
||||||
.katex-same-height-as-text .katex {
|
|
||||||
font-size: 1.0em;
|
|
||||||
}
|
}
|
|
@ -51,6 +51,7 @@
|
||||||
.part-table-image {
|
.part-table-image {
|
||||||
max-height: 40px;
|
max-height: 40px;
|
||||||
object-fit: contain;
|
object-fit: contain;
|
||||||
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.part-info-image {
|
.part-info-image {
|
||||||
|
@ -60,4 +61,4 @@
|
||||||
|
|
||||||
.object-fit-cover {
|
.object-fit-cover {
|
||||||
object-fit: cover;
|
object-fit: cover;
|
||||||
}
|
}
|
|
@ -108,8 +108,8 @@ body {
|
||||||
.back-to-top {
|
.back-to-top {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
position: fixed;
|
position: fixed;
|
||||||
bottom: 60px;
|
bottom: 20px;
|
||||||
right: 40px;
|
right: 20px;
|
||||||
display:none;
|
display:none;
|
||||||
z-index: 1030;
|
z-index: 1030;
|
||||||
}
|
}
|
||||||
|
|
|
@ -63,6 +63,10 @@ table.dataTable > tbody > tr.selected > td > a {
|
||||||
margin-block-end: 0;
|
margin-block-end: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.card-footer-table {
|
||||||
|
padding-top: 0;
|
||||||
|
}
|
||||||
|
|
||||||
table.dataTable {
|
table.dataTable {
|
||||||
margin-top: 0 !important;
|
margin-top: 0 !important;
|
||||||
}
|
}
|
||||||
|
@ -80,7 +84,7 @@ th.select-checkbox {
|
||||||
* Datatables definitions/overrides
|
* Datatables definitions/overrides
|
||||||
********************************************************************/
|
********************************************************************/
|
||||||
|
|
||||||
.dt-length {
|
.dataTables_length {
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -90,16 +94,6 @@ table.dataTable tr.selected td.select-checkbox:after
|
||||||
margin-top: -20px !important;
|
margin-top: -20px !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Show pagination right aligned */
|
|
||||||
.dt-paging .pagination {
|
|
||||||
justify-content: flex-end;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Fix table row coloring */
|
|
||||||
table.table.dataTable > :not(caption) > * > * {
|
|
||||||
background-color: var(--bs-table-bg);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/******************************************************
|
/******************************************************
|
||||||
Classes for Datatables export
|
Classes for Datatables export
|
||||||
|
@ -109,4 +103,53 @@ Classes for Datatables export
|
||||||
#export-messageTop,
|
#export-messageTop,
|
||||||
.export-helper{
|
.export-helper{
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/******************************************************
|
||||||
|
* Styling for the select all checkbox in the parts table
|
||||||
|
* Should match the styling of the select checkbox
|
||||||
|
******************************************************/
|
||||||
|
table.dataTable > thead > tr > th.select-checkbox {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
table.dataTable > thead > tr > th.select-checkbox:before,
|
||||||
|
table.dataTable > thead > tr > th.select-checkbox:after {
|
||||||
|
display: block;
|
||||||
|
position: absolute;
|
||||||
|
top: 0.9em;
|
||||||
|
left: 50%;
|
||||||
|
width: 1em !important;
|
||||||
|
height: 1em !important;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
table.dataTable > thead > tr > th.select-checkbox:before {
|
||||||
|
content: " ";
|
||||||
|
margin-top: -5px;
|
||||||
|
margin-left: -6px;
|
||||||
|
border: 2px solid var(--bs-tertiary-color);
|
||||||
|
border-radius: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
table.dataTable > tbody > tr > td.select-checkbox:before, table.dataTable > tbody > tr > th.select-checkbox:before {
|
||||||
|
border: 2px solid var(--bs-tertiary-color) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
table.dataTable > tbody > tr > td.select-checkbox:before, table.dataTable > tbody > tr > td.select-checkbox:after, table.dataTable > tbody > tr > th.select-checkbox:before, table.dataTable > tbody > tr > th.select-checkbox:after {
|
||||||
|
width: 1em !important;
|
||||||
|
height: 1em !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
table.dataTable > thead > tr.selected > th.select-checkbox:after {
|
||||||
|
content: "✓";
|
||||||
|
font-size: 20px;
|
||||||
|
margin-top: -20px;
|
||||||
|
margin-left: -6px;
|
||||||
|
text-align: center;
|
||||||
|
/*text-shadow: 1px 1px #B0BED9, -1px -1px #B0BED9, 1px -1px #B0BED9, -1px 1px #B0BED9; */
|
||||||
|
}
|
||||||
|
table.dataTable.compact > thead > tr > th.select-checkbox:before {
|
||||||
|
margin-top: -12px;
|
||||||
|
}
|
||||||
|
table.dataTable.compact > thead > tr.selected > th.select-checkbox:after {
|
||||||
|
margin-top: -16px;
|
||||||
|
}
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -24,8 +24,9 @@
|
||||||
/** Should be the same settings, as in label_style.css */
|
/** Should be the same settings, as in label_style.css */
|
||||||
.ck-html-label .ck-content {
|
.ck-html-label .ck-content {
|
||||||
font-family: "DejaVu Sans Mono", monospace;
|
font-family: "DejaVu Sans Mono", monospace;
|
||||||
font-size: 12pt;
|
font-size: 12px;
|
||||||
line-height: 1.0;
|
line-height: 1.0;
|
||||||
|
font-size-adjust: 1.5;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ck-html-label .ck-content p {
|
.ck-html-label .ck-content p {
|
||||||
|
|
|
@ -1,69 +0,0 @@
|
||||||
/******************************************************************************************
|
|
||||||
* This styles the checkboxes of the select extension exactly like the ones in bootstrap 5
|
|
||||||
******************************************************************************************/
|
|
||||||
|
|
||||||
table.dataTable > tbody > tr > .selected {
|
|
||||||
background-color: var(--bs-primary-bg-subtle) !important;
|
|
||||||
color: white;
|
|
||||||
}
|
|
||||||
table.dataTable > tbody > tr > .dt-select {
|
|
||||||
text-align: center;
|
|
||||||
vertical-align: middle;
|
|
||||||
}
|
|
||||||
table.dataTable > thead > tr > .dt-select {
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
table.dataTable input.dt-select-checkbox {
|
|
||||||
--bs-form-check-bg: var(--bs-body-bg);
|
|
||||||
flex-shrink: 0;
|
|
||||||
width: 1em;
|
|
||||||
height: 1em;
|
|
||||||
margin-top: 0.25em;
|
|
||||||
vertical-align: top;
|
|
||||||
-webkit-appearance: none;
|
|
||||||
-moz-appearance: none;
|
|
||||||
appearance: none;
|
|
||||||
background-color: var(--bs-form-check-bg);
|
|
||||||
background-image: var(--bs-form-check-bg-image);
|
|
||||||
background-repeat: no-repeat;
|
|
||||||
background-position: center;
|
|
||||||
background-size: contain;
|
|
||||||
border: var(--bs-border-width) solid var(--bs-border-color);
|
|
||||||
-webkit-print-color-adjust: exact;
|
|
||||||
color-adjust: exact;
|
|
||||||
print-color-adjust: exact;
|
|
||||||
border-radius: 0.25em;
|
|
||||||
}
|
|
||||||
|
|
||||||
table.dataTable input.dt-select-checkbox:checked {
|
|
||||||
background-color: rgb(var(--bs-secondary-rgb));
|
|
||||||
border-color: rgb(var(--bs-secondary-rgb));
|
|
||||||
--bs-form-check-bg-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'%3e%3cpath fill='none' stroke='%23fff' stroke-linecap='round' stroke-linejoin='round' stroke-width='3' d='m6 10 3 3 6-6'/%3e%3c/svg%3e");
|
|
||||||
}
|
|
||||||
|
|
||||||
table.dataTable input.dt-select-checkbox:indeterminate {
|
|
||||||
background-color: rgb(var(--bs-secondary-rgb));
|
|
||||||
border-color: rgb(var(--bs-secondary-rgb));
|
|
||||||
--bs-form-check-bg-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'%3e%3cpath fill='none' stroke='%23fff' stroke-linecap='round' stroke-linejoin='round' stroke-width='3' d='M6 10h8'/%3e%3c/svg%3e");
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
div.dt-container span.select-info,
|
|
||||||
div.dt-container span.select-item {
|
|
||||||
margin-left: 0.5em;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@media screen and (max-width: 640px) {
|
|
||||||
div.dt-container span.select-info,
|
|
||||||
div.dt-container span.select-item {
|
|
||||||
margin-left: 0;
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
table.dataTable.table-sm tbody td.select-checkbox::before {
|
|
||||||
margin-top: -9px;
|
|
||||||
}
|
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -44,18 +44,4 @@ import "./register_events";
|
||||||
import "./tristate_checkboxes";
|
import "./tristate_checkboxes";
|
||||||
|
|
||||||
//Define jquery globally
|
//Define jquery globally
|
||||||
window.$ = window.jQuery = require("jquery");
|
window.$ = window.jQuery = require("jquery")
|
||||||
|
|
||||||
//Use the local WASM file for the ZXing library
|
|
||||||
import {
|
|
||||||
setZXingModuleOverrides,
|
|
||||||
} from "barcode-detector/pure";
|
|
||||||
import wasmFile from "../../node_modules/zxing-wasm/dist/reader/zxing_reader.wasm";
|
|
||||||
setZXingModuleOverrides({
|
|
||||||
locateFile: (path, prefix) => {
|
|
||||||
if (path.endsWith(".wasm")) {
|
|
||||||
return wasmFile;
|
|
||||||
}
|
|
||||||
return prefix + path;
|
|
||||||
},
|
|
||||||
});
|
|
File diff suppressed because it is too large
Load diff
|
@ -98,15 +98,6 @@
|
||||||
dtOpts = config.options(dtOpts);
|
dtOpts = config.options(dtOpts);
|
||||||
}
|
}
|
||||||
|
|
||||||
//Choose the column where the className contains "select-column" and apply the select extension to its render field
|
|
||||||
//Added for Part-DB
|
|
||||||
for (let column of dtOpts.columns) {
|
|
||||||
if (column.className && column.className.includes('dt-select')) {
|
|
||||||
column.render = $.fn.dataTable.render.select();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
root.html(data.template);
|
root.html(data.template);
|
||||||
dt = $('table', root).DataTable(dtOpts);
|
dt = $('table', root).DataTable(dtOpts);
|
||||||
if (config.state !== 'none') {
|
if (config.state !== 'none') {
|
||||||
|
|
|
@ -20,8 +20,6 @@
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
import {Dropdown} from "bootstrap";
|
import {Dropdown} from "bootstrap";
|
||||||
import ClipboardJS from "clipboard";
|
|
||||||
import {Modal} from "bootstrap";
|
|
||||||
|
|
||||||
class RegisterEventHelper {
|
class RegisterEventHelper {
|
||||||
constructor() {
|
constructor() {
|
||||||
|
@ -29,14 +27,7 @@ class RegisterEventHelper {
|
||||||
this.configureDropdowns();
|
this.configureDropdowns();
|
||||||
this.registerSpecialCharInput();
|
this.registerSpecialCharInput();
|
||||||
|
|
||||||
//Initialize ClipboardJS
|
|
||||||
this.registerLoadHandler(() => {
|
|
||||||
new ClipboardJS('.btn');
|
|
||||||
});
|
|
||||||
|
|
||||||
this.registerModalDropRemovalOnFormSubmit();
|
this.registerModalDropRemovalOnFormSubmit();
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
registerModalDropRemovalOnFormSubmit() {
|
registerModalDropRemovalOnFormSubmit() {
|
||||||
|
@ -46,15 +37,6 @@ class RegisterEventHelper {
|
||||||
if (back_drop) {
|
if (back_drop) {
|
||||||
back_drop.remove();
|
back_drop.remove();
|
||||||
}
|
}
|
||||||
|
|
||||||
//Remove scroll-lock if it is still active
|
|
||||||
if (document.body.classList.contains('modal-open')) {
|
|
||||||
document.body.classList.remove('modal-open');
|
|
||||||
|
|
||||||
//Remove the padding-right and overflow:hidden from the body
|
|
||||||
document.body.style.paddingRight = '';
|
|
||||||
document.body.style.overflow = '';
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -19,7 +19,7 @@
|
||||||
|
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
import {Tab, Dropdown, Collapse} from "bootstrap";
|
import {Tab, Dropdown} from "bootstrap";
|
||||||
import tab from "bootstrap/js/src/tab";
|
import tab from "bootstrap/js/src/tab";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -54,7 +54,6 @@ class TabRememberHelper {
|
||||||
const first_element = merged[0] ?? null;
|
const first_element = merged[0] ?? null;
|
||||||
if(first_element) {
|
if(first_element) {
|
||||||
this.revealElementOnTab(first_element);
|
this.revealElementOnTab(first_element);
|
||||||
this.revealElementInCollapse(first_element);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -63,20 +62,10 @@ class TabRememberHelper {
|
||||||
* @param event
|
* @param event
|
||||||
*/
|
*/
|
||||||
onInvalid(event) {
|
onInvalid(event) {
|
||||||
this.revealElementInCollapse(event.target);
|
|
||||||
this.revealElementOnTab(event.target);
|
this.revealElementOnTab(event.target);
|
||||||
this.revealElementInDropdown(event.target);
|
this.revealElementInDropdown(event.target);
|
||||||
}
|
}
|
||||||
|
|
||||||
revealElementInCollapse(element) {
|
|
||||||
let collapse = element.closest('.collapse');
|
|
||||||
|
|
||||||
if(collapse) {
|
|
||||||
let bs_collapse = Collapse.getOrCreateInstance(collapse);
|
|
||||||
bs_collapse.show();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
revealElementInDropdown(element) {
|
revealElementInDropdown(element) {
|
||||||
let dropdown = element.closest('.dropdown-menu');
|
let dropdown = element.closest('.dropdown-menu');
|
||||||
|
|
||||||
|
|
|
@ -1,63 +0,0 @@
|
||||||
/**
|
|
||||||
* Autoselect Typed plugin for Tomselect
|
|
||||||
*
|
|
||||||
* This plugin allows automatically selecting an option matching the typed text when the Tomselect element goes out of
|
|
||||||
* focus (is blurred) and/or when the delimiter is typed.
|
|
||||||
*
|
|
||||||
* #select_on_blur option
|
|
||||||
* Tomselect natively supports the "createOnBlur" option. This option picks up any remaining text in the input field
|
|
||||||
* and uses it to create a new option and selects that option. It does behave a bit strangely though, in that it will
|
|
||||||
* not select an already existing option when the input is blurred, so if you typed something that matches an option in
|
|
||||||
* the list and then click outside the box (without pressing enter) the entered text is just removed (unless you have
|
|
||||||
* allow duplicates on in which case it will create a new option).
|
|
||||||
* This plugin fixes that, such that Tomselect will first try to select an option matching the remaining uncommitted
|
|
||||||
* text and only when no matching option is found tries to create a new one (if createOnBlur and create is on)
|
|
||||||
*
|
|
||||||
* #select_on_delimiter option
|
|
||||||
* Normally when typing the delimiter (space by default) Tomselect will try to create a new option (and select it) (if
|
|
||||||
* create is on), but if the typed text matches an option (and allow duplicates is off) it refuses to react at all until
|
|
||||||
* you press enter. With this option, the delimiter will also allow selecting an option, not just creating it.
|
|
||||||
*/
|
|
||||||
function select_current_input(self){
|
|
||||||
if(self.isLocked){
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
const val = self.inputValue()
|
|
||||||
//Do nothing if the input is empty
|
|
||||||
if (!val) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if (self.options[val]) {
|
|
||||||
self.addItem(val)
|
|
||||||
self.setTextboxValue()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default function(plugin_options_) {
|
|
||||||
const plugin_options = Object.assign({
|
|
||||||
//Autoselect the typed text when the input element goes out of focus
|
|
||||||
select_on_blur: true,
|
|
||||||
//Autoselect the typed text when the delimiter is typed
|
|
||||||
select_on_delimiter: true,
|
|
||||||
}, plugin_options_);
|
|
||||||
|
|
||||||
const self = this
|
|
||||||
|
|
||||||
if(plugin_options.select_on_blur) {
|
|
||||||
this.hook("before", "onBlur", function () {
|
|
||||||
select_current_input(self)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
if(plugin_options.select_on_delimiter) {
|
|
||||||
this.hook("before", "onKeyPress", function (e) {
|
|
||||||
const character = String.fromCharCode(e.keyCode || e.which);
|
|
||||||
if (self.settings.mode === 'multi' && character === self.settings.delimiter) {
|
|
||||||
select_current_input(self)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,93 +0,0 @@
|
||||||
/**
|
|
||||||
* click_to_edit plugin for Tomselect
|
|
||||||
*
|
|
||||||
* This plugin allows editing (and selecting text in) any selected item by clicking it.
|
|
||||||
*
|
|
||||||
* Usually, when the user typed some text and created an item in Tomselect that item cannot be edited anymore. To make
|
|
||||||
* a change, the item has to be deleted and retyped completely. There is also generally no way to copy text out of a
|
|
||||||
* tomselect item. The "restore_on_backspace" plugin improves that somewhat, by allowing the user to edit an item after
|
|
||||||
* pressing backspace. However, it is somewhat confusing to first have to focus the field an then hit backspace in order
|
|
||||||
* to copy a piece of text. It may also not be immediately obvious for editing.
|
|
||||||
* This plugin transforms an item into editable text when it is clicked, e.g. when the user tries to place the caret
|
|
||||||
* within an item or when they try to drag across the text to highlight it.
|
|
||||||
* It also plays nice with the remove_button plugin which still removes (deselects) an option entirely.
|
|
||||||
*
|
|
||||||
* It is recommended to also enable the autoselect_typed plugin when using this plugin. Without it, the text in the
|
|
||||||
* input field (i.e. the item that was just clicked) is lost when the user clicks outside the field. Also, when the user
|
|
||||||
* clicks an option (making it text) and then tries to enter another one by entering the delimiter (e.g. space) nothing
|
|
||||||
* happens until enter is pressed or the text is changed from what it was.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return a dom element from either a dom query string, jQuery object, a dom element or html string
|
|
||||||
* https://stackoverflow.com/questions/494143/creating-a-new-dom-element-from-an-html-string-using-built-in-dom-methods-or-pro/35385518#35385518
|
|
||||||
*
|
|
||||||
* param query should be {}
|
|
||||||
*/
|
|
||||||
const getDom = query => {
|
|
||||||
if (query.jquery) {
|
|
||||||
return query[0];
|
|
||||||
}
|
|
||||||
if (query instanceof HTMLElement) {
|
|
||||||
return query;
|
|
||||||
}
|
|
||||||
if (isHtmlString(query)) {
|
|
||||||
var tpl = document.createElement('template');
|
|
||||||
tpl.innerHTML = query.trim(); // Never return a text node of whitespace as the result
|
|
||||||
return tpl.content.firstChild;
|
|
||||||
}
|
|
||||||
return document.querySelector(query);
|
|
||||||
};
|
|
||||||
const isHtmlString = arg => {
|
|
||||||
if (typeof arg === 'string' && arg.indexOf('<') > -1) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
};
|
|
||||||
|
|
||||||
function plugin(plugin_options_) {
|
|
||||||
const self = this
|
|
||||||
|
|
||||||
const plugin_options = Object.assign({
|
|
||||||
//If there is unsubmitted text in the input field, should that text be automatically used to select a matching
|
|
||||||
//element? If this is off, clicking on item1 and then clicking on item2 will result in item1 being deselected
|
|
||||||
auto_select_before_edit: true,
|
|
||||||
//If there is unsubmitted text in the input field, should that text be automatically used to create a matching
|
|
||||||
//element if no matching element was found or auto_select_before_edit is off?
|
|
||||||
auto_create_before_edit: true,
|
|
||||||
//customize this function to change which text the item is replaced with when clicking on it
|
|
||||||
text: option => {
|
|
||||||
return option[self.settings.labelField];
|
|
||||||
}
|
|
||||||
}, plugin_options_);
|
|
||||||
|
|
||||||
|
|
||||||
self.hook('after', 'setupTemplates', () => {
|
|
||||||
const orig_render_item = self.settings.render.item;
|
|
||||||
self.settings.render.item = (data, escape) => {
|
|
||||||
const item = getDom(orig_render_item.call(self, data, escape));
|
|
||||||
|
|
||||||
item.addEventListener('click', evt => {
|
|
||||||
if (self.isLocked) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const val = self.inputValue();
|
|
||||||
|
|
||||||
if (self.options[val]) {
|
|
||||||
self.addItem(val)
|
|
||||||
} else if (self.settings.create) {
|
|
||||||
self.createItem();
|
|
||||||
}
|
|
||||||
const option = self.options[item.dataset.value]
|
|
||||||
self.setTextboxValue(plugin_options.text.call(self, option));
|
|
||||||
self.focus();
|
|
||||||
self.removeItem(item);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
return item;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
}
|
|
||||||
export { plugin as default };
|
|
11
bin/console
11
bin/console
|
@ -4,17 +4,6 @@
|
||||||
use App\Kernel;
|
use App\Kernel;
|
||||||
use Symfony\Bundle\FrameworkBundle\Console\Application;
|
use Symfony\Bundle\FrameworkBundle\Console\Application;
|
||||||
|
|
||||||
if (!is_dir(dirname(__DIR__).'/vendor')) {
|
|
||||||
throw new LogicException('Dependencies are missing. Try running "composer install".');
|
|
||||||
}
|
|
||||||
|
|
||||||
//Increase xdebug.max_nesting_level to 1000 if required (see issue #411)
|
|
||||||
//Check if xdebug extension is active, and xdebug.max_nesting_level is set to 256 or lower
|
|
||||||
if (extension_loaded('xdebug') && ((int) ini_get('xdebug.max_nesting_level')) <= 256) {
|
|
||||||
//Increase xdebug.max_nesting_level to 1000
|
|
||||||
ini_set('xdebug.max_nesting_level', '1000');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!is_file(dirname(__DIR__).'/vendor/autoload_runtime.php')) {
|
if (!is_file(dirname(__DIR__).'/vendor/autoload_runtime.php')) {
|
||||||
throw new LogicException('Symfony Runtime is missing. Try running "composer require symfony/runtime".');
|
throw new LogicException('Symfony Runtime is missing. Try running "composer require symfony/runtime".');
|
||||||
}
|
}
|
||||||
|
|
10
bin/phpunit
10
bin/phpunit
|
@ -6,13 +6,9 @@ if (!ini_get('date.timezone')) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (is_file(dirname(__DIR__).'/vendor/phpunit/phpunit/phpunit')) {
|
if (is_file(dirname(__DIR__).'/vendor/phpunit/phpunit/phpunit')) {
|
||||||
if (PHP_VERSION_ID >= 80000) {
|
define('PHPUNIT_COMPOSER_INSTALL', dirname(__DIR__).'/vendor/autoload.php');
|
||||||
require dirname(__DIR__).'/vendor/phpunit/phpunit/phpunit';
|
require PHPUNIT_COMPOSER_INSTALL;
|
||||||
} else {
|
PHPUnit\TextUI\Command::main();
|
||||||
define('PHPUNIT_COMPOSER_INSTALL', dirname(__DIR__).'/vendor/autoload.php');
|
|
||||||
require PHPUNIT_COMPOSER_INSTALL;
|
|
||||||
PHPUnit\TextUI\Command::main();
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
if (!is_file(dirname(__DIR__).'/vendor/symfony/phpunit-bridge/bin/simple-phpunit.php')) {
|
if (!is_file(dirname(__DIR__).'/vendor/symfony/phpunit-bridge/bin/simple-phpunit.php')) {
|
||||||
echo "Unable to find the `simple-phpunit.php` script in `vendor/symfony/phpunit-bridge/bin/`.\n";
|
echo "Unable to find the `simple-phpunit.php` script in `vendor/symfony/phpunit-bridge/bin/`.\n";
|
||||||
|
|
|
@ -5,5 +5,4 @@ coverage:
|
||||||
status:
|
status:
|
||||||
project:
|
project:
|
||||||
default:
|
default:
|
||||||
threshold: 10%
|
threshold: 5%
|
||||||
target: 40%
|
|
124
composer.json
124
composer.json
|
@ -1,5 +1,4 @@
|
||||||
{
|
{
|
||||||
"name": "part-db/part-db-server",
|
|
||||||
"type": "project",
|
"type": "project",
|
||||||
"license": "AGPL-3.0-or-later",
|
"license": "AGPL-3.0-or-later",
|
||||||
"require": {
|
"require": {
|
||||||
|
@ -11,24 +10,21 @@
|
||||||
"ext-intl": "*",
|
"ext-intl": "*",
|
||||||
"ext-json": "*",
|
"ext-json": "*",
|
||||||
"ext-mbstring": "*",
|
"ext-mbstring": "*",
|
||||||
"amphp/http-client": "^5.1",
|
|
||||||
"api-platform/core": "^3.1",
|
|
||||||
"beberlei/doctrineextensions": "^1.2",
|
"beberlei/doctrineextensions": "^1.2",
|
||||||
"brick/math": "0.12.1 as 0.11.0",
|
"brick/math": "^0.11.0",
|
||||||
"composer/ca-bundle": "^1.5",
|
|
||||||
"composer/package-versions-deprecated": "^1.11.99.5",
|
"composer/package-versions-deprecated": "^1.11.99.5",
|
||||||
"doctrine/data-fixtures": "^2.0.0",
|
"doctrine/annotations": "1.14.3",
|
||||||
"doctrine/dbal": "^4.0.0",
|
"doctrine/data-fixtures": "^1.6.6",
|
||||||
|
"doctrine/dbal": "^3.4.6",
|
||||||
"doctrine/doctrine-bundle": "^2.0",
|
"doctrine/doctrine-bundle": "^2.0",
|
||||||
"doctrine/doctrine-migrations-bundle": "^3.0",
|
"doctrine/doctrine-migrations-bundle": "^3.0",
|
||||||
"doctrine/orm": "^3.2.0",
|
"doctrine/orm": "^2.16",
|
||||||
"dompdf/dompdf": "^v3.0.0",
|
"dompdf/dompdf": "dev-master#87bea32efe0b0db309e1d31537201f64d5508280 as v2.0.3",
|
||||||
"erusev/parsedown": "^1.7",
|
"erusev/parsedown": "^1.7",
|
||||||
"florianv/swap": "^4.0",
|
"florianv/swap": "^4.0",
|
||||||
"florianv/swap-bundle": "dev-master",
|
"florianv/swap-bundle": "dev-master",
|
||||||
"gregwar/captcha-bundle": "^2.1.0",
|
"gregwar/captcha-bundle": "^2.1.0",
|
||||||
"hshn/base64-encoded-file": "^5.0",
|
"jbtronics/2fa-webauthn": "^v2.0.0",
|
||||||
"jbtronics/2fa-webauthn": "^v2.2.0",
|
|
||||||
"jbtronics/dompdf-font-loader-bundle": "^1.0.0",
|
"jbtronics/dompdf-font-loader-bundle": "^1.0.0",
|
||||||
"jfcherng/php-diff": "^6.14",
|
"jfcherng/php-diff": "^6.14",
|
||||||
"knpuniversity/oauth2-client-bundle": "^2.15",
|
"knpuniversity/oauth2-client-bundle": "^2.15",
|
||||||
|
@ -37,14 +33,13 @@
|
||||||
"liip/imagine-bundle": "^2.2",
|
"liip/imagine-bundle": "^2.2",
|
||||||
"nbgrp/onelogin-saml-bundle": "^1.3",
|
"nbgrp/onelogin-saml-bundle": "^1.3",
|
||||||
"nelexa/zip": "^4.0",
|
"nelexa/zip": "^4.0",
|
||||||
"nelmio/cors-bundle": "^2.3",
|
|
||||||
"nelmio/security-bundle": "^3.0",
|
"nelmio/security-bundle": "^3.0",
|
||||||
"nyholm/psr7": "^1.1",
|
"nyholm/psr7": "^1.1",
|
||||||
"omines/datatables-bundle": "^0.9.1",
|
"ocramius/proxy-manager": "2.2.*",
|
||||||
"paragonie/sodium_compat": "^1.21",
|
"omines/datatables-bundle": "^0.7.2",
|
||||||
"part-db/label-fonts": "^1.0",
|
"part-db/label-fonts": "^1.0",
|
||||||
"rhukster/dom-sanitizer": "^1.0",
|
"php-translation/symfony-bundle": "^0.14.0",
|
||||||
"runtime/frankenphp-symfony": "^0.2.0",
|
"phpdocumentor/reflection-docblock": "^5.2",
|
||||||
"s9e/text-formatter": "^2.1",
|
"s9e/text-formatter": "^2.1",
|
||||||
"scheb/2fa-backup-code": "^6.8.0",
|
"scheb/2fa-backup-code": "^6.8.0",
|
||||||
"scheb/2fa-bundle": "^6.8.0",
|
"scheb/2fa-bundle": "^6.8.0",
|
||||||
|
@ -53,66 +48,64 @@
|
||||||
"shivas/versioning-bundle": "^4.0",
|
"shivas/versioning-bundle": "^4.0",
|
||||||
"spatie/db-dumper": "^3.3.1",
|
"spatie/db-dumper": "^3.3.1",
|
||||||
"symfony/apache-pack": "^1.0",
|
"symfony/apache-pack": "^1.0",
|
||||||
"symfony/asset": "6.4.*",
|
"symfony/asset": "6.3.*",
|
||||||
"symfony/console": "6.4.*",
|
"symfony/console": "6.3.*",
|
||||||
"symfony/css-selector": "6.4.*",
|
"symfony/dotenv": "6.3.*",
|
||||||
"symfony/dom-crawler": "6.4.*",
|
"symfony/expression-language": "6.3.*",
|
||||||
"symfony/dotenv": "6.4.*",
|
|
||||||
"symfony/expression-language": "6.4.*",
|
|
||||||
"symfony/flex": "^v2.3.1",
|
"symfony/flex": "^v2.3.1",
|
||||||
"symfony/form": "6.4.*",
|
"symfony/form": "6.3.*",
|
||||||
"symfony/framework-bundle": "6.4.*",
|
"symfony/framework-bundle": "6.3.*",
|
||||||
"symfony/http-client": "6.4.*",
|
"symfony/http-client": "6.3.*",
|
||||||
"symfony/http-kernel": "6.4.*",
|
"symfony/http-kernel": "6.3.*",
|
||||||
"symfony/mailer": "6.4.*",
|
"symfony/mailer": "6.3.*",
|
||||||
"symfony/monolog-bundle": "^3.1",
|
"symfony/monolog-bundle": "^3.1",
|
||||||
"symfony/polyfill-php82": "^1.28",
|
"symfony/process": "6.3.*",
|
||||||
"symfony/process": "6.4.*",
|
"symfony/property-access": "6.3.*",
|
||||||
"symfony/property-access": "6.4.*",
|
"symfony/property-info": "6.3.*",
|
||||||
"symfony/property-info": "6.4.*",
|
"symfony/proxy-manager-bridge": "6.3.*",
|
||||||
"symfony/rate-limiter": "6.4.*",
|
"symfony/rate-limiter": "6.3.*",
|
||||||
"symfony/runtime": "6.4.*",
|
"symfony/runtime": "6.3.*",
|
||||||
"symfony/security-bundle": "6.4.*",
|
"symfony/security-bundle": "6.3.*",
|
||||||
"symfony/serializer": "6.4.*",
|
"symfony/serializer": "6.3.*",
|
||||||
"symfony/string": "6.4.*",
|
"symfony/translation": "6.3.*",
|
||||||
"symfony/translation": "6.4.*",
|
"symfony/twig-bundle": "6.3.*",
|
||||||
"symfony/twig-bundle": "6.4.*",
|
|
||||||
"symfony/ux-translator": "^2.10",
|
"symfony/ux-translator": "^2.10",
|
||||||
"symfony/ux-turbo": "^2.0",
|
"symfony/ux-turbo": "^2.0",
|
||||||
"symfony/validator": "6.4.*",
|
"symfony/validator": "6.3.*",
|
||||||
"symfony/web-link": "6.4.*",
|
"symfony/web-link": "6.3.*",
|
||||||
"symfony/webpack-encore-bundle": "^v2.0.1",
|
"symfony/webpack-encore-bundle": "^v2.0.1",
|
||||||
"symfony/yaml": "6.4.*",
|
"symfony/yaml": "6.3.*",
|
||||||
"tecnickcom/tc-lib-barcode": "^2.1.4",
|
"tecnickcom/tc-lib-barcode": "^1.15",
|
||||||
"twig/cssinliner-extra": "^3.0",
|
"twig/cssinliner-extra": "^3.0",
|
||||||
"twig/extra-bundle": "^3.8",
|
"twig/extra-bundle": "^3.0",
|
||||||
"twig/html-extra": "^3.8",
|
"twig/html-extra": "^3.0",
|
||||||
"twig/inky-extra": "^3.0",
|
"twig/inky-extra": "^3.0",
|
||||||
"twig/intl-extra": "^3.8",
|
"twig/intl-extra": "^3.0",
|
||||||
"twig/markdown-extra": "^3.8",
|
"twig/markdown-extra": "^3.0",
|
||||||
"twig/string-extra": "^3.8",
|
"web-auth/webauthn-symfony-bundle": "^4.0.0",
|
||||||
"web-auth/webauthn-symfony-bundle": "^4.0.0"
|
"webmozart/assert": "^1.4"
|
||||||
},
|
},
|
||||||
"require-dev": {
|
"require-dev": {
|
||||||
"dama/doctrine-test-bundle": "^v8.0.0",
|
"dama/doctrine-test-bundle": "^7.0",
|
||||||
"doctrine/doctrine-fixtures-bundle": "^4.0.0",
|
"doctrine/doctrine-fixtures-bundle": "^3.2",
|
||||||
"ekino/phpstan-banned-code": "^v3.0.0",
|
"ekino/phpstan-banned-code": "^v1.0.0",
|
||||||
"jbtronics/translation-editor-bundle": "^1.0",
|
|
||||||
"phpstan/extension-installer": "^1.0",
|
"phpstan/extension-installer": "^1.0",
|
||||||
"phpstan/phpstan": "^2.0.4",
|
"phpstan/phpstan": "^1.4.7",
|
||||||
"phpstan/phpstan-doctrine": "^2.0.1",
|
"phpstan/phpstan-doctrine": "^1.2.11",
|
||||||
"phpstan/phpstan-strict-rules": "^2.0.1",
|
"phpstan/phpstan-strict-rules": "^1.5",
|
||||||
"phpstan/phpstan-symfony": "^2.0.0",
|
"phpstan/phpstan-symfony": "^1.1.7",
|
||||||
"phpunit/phpunit": "^9.5",
|
"psalm/plugin-symfony": "^v5.0.1",
|
||||||
"rector/rector": "^2.0.4",
|
"rector/rector": "^0.18.0",
|
||||||
"roave/security-advisories": "dev-latest",
|
"roave/security-advisories": "dev-latest",
|
||||||
"symfony/browser-kit": "6.4.*",
|
"symfony/browser-kit": "6.3.*",
|
||||||
"symfony/debug-bundle": "6.4.*",
|
"symfony/css-selector": "6.3.*",
|
||||||
|
"symfony/debug-bundle": "6.3.*",
|
||||||
"symfony/maker-bundle": "^1.13",
|
"symfony/maker-bundle": "^1.13",
|
||||||
"symfony/phpunit-bridge": "6.4.*",
|
"symfony/phpunit-bridge": "6.3.*",
|
||||||
"symfony/stopwatch": "6.4.*",
|
"symfony/stopwatch": "6.3.*",
|
||||||
"symfony/web-profiler-bundle": "6.4.*",
|
"symfony/web-profiler-bundle": "6.3.*",
|
||||||
"symplify/easy-coding-standard": "^12.0"
|
"symplify/easy-coding-standard": "^12.0",
|
||||||
|
"vimeo/psalm": "^5.6.0"
|
||||||
},
|
},
|
||||||
"suggest": {
|
"suggest": {
|
||||||
"ext-bcmath": "Used to improve price calculation performance",
|
"ext-bcmath": "Used to improve price calculation performance",
|
||||||
|
@ -163,8 +156,7 @@
|
||||||
"extra": {
|
"extra": {
|
||||||
"symfony": {
|
"symfony": {
|
||||||
"allow-contrib": false,
|
"allow-contrib": false,
|
||||||
"require": "6.4.*",
|
"require": "6.3.*"
|
||||||
"docker": true
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
10818
composer.lock
generated
10818
composer.lock
generated
File diff suppressed because it is too large
Load diff
|
@ -18,18 +18,17 @@ return [
|
||||||
DAMA\DoctrineTestBundle\DAMADoctrineTestBundle::class => ['test' => true],
|
DAMA\DoctrineTestBundle\DAMADoctrineTestBundle::class => ['test' => true],
|
||||||
Twig\Extra\TwigExtraBundle\TwigExtraBundle::class => ['all' => true],
|
Twig\Extra\TwigExtraBundle\TwigExtraBundle::class => ['all' => true],
|
||||||
Gregwar\CaptchaBundle\GregwarCaptchaBundle::class => ['all' => true],
|
Gregwar\CaptchaBundle\GregwarCaptchaBundle::class => ['all' => true],
|
||||||
|
Translation\Bundle\TranslationBundle::class => ['all' => true],
|
||||||
Florianv\SwapBundle\FlorianvSwapBundle::class => ['all' => true],
|
Florianv\SwapBundle\FlorianvSwapBundle::class => ['all' => true],
|
||||||
Nelmio\SecurityBundle\NelmioSecurityBundle::class => ['all' => true],
|
Nelmio\SecurityBundle\NelmioSecurityBundle::class => ['all' => true],
|
||||||
Symfony\UX\Turbo\TurboBundle::class => ['all' => true],
|
Symfony\UX\Turbo\TurboBundle::class => ['all' => true],
|
||||||
Jbtronics\TFAWebauthn\TFAWebauthnBundle::class => ['all' => true],
|
Jbtronics\TFAWebauthn\TFAWebauthnBundle::class => ['all' => true],
|
||||||
Scheb\TwoFactorBundle\SchebTwoFactorBundle::class => ['all' => true],
|
Scheb\TwoFactorBundle\SchebTwoFactorBundle::class => ['all' => true],
|
||||||
|
SpomkyLabs\CborBundle\SpomkyLabsCborBundle::class => ['all' => true],
|
||||||
Webauthn\Bundle\WebauthnBundle::class => ['all' => true],
|
Webauthn\Bundle\WebauthnBundle::class => ['all' => true],
|
||||||
Nbgrp\OneloginSamlBundle\NbgrpOneloginSamlBundle::class => ['all' => true],
|
Nbgrp\OneloginSamlBundle\NbgrpOneloginSamlBundle::class => ['all' => true],
|
||||||
Symfony\UX\StimulusBundle\StimulusBundle::class => ['all' => true],
|
Symfony\UX\StimulusBundle\StimulusBundle::class => ['all' => true],
|
||||||
Symfony\UX\Translator\UxTranslatorBundle::class => ['all' => true],
|
Symfony\UX\Translator\UxTranslatorBundle::class => ['all' => true],
|
||||||
Jbtronics\DompdfFontLoaderBundle\DompdfFontLoaderBundle::class => ['all' => true],
|
Jbtronics\DompdfFontLoaderBundle\DompdfFontLoaderBundle::class => ['all' => true],
|
||||||
KnpU\OAuth2ClientBundle\KnpUOAuth2ClientBundle::class => ['all' => true],
|
KnpU\OAuth2ClientBundle\KnpUOAuth2ClientBundle::class => ['all' => true],
|
||||||
Nelmio\CorsBundle\NelmioCorsBundle::class => ['all' => true],
|
|
||||||
ApiPlatform\Symfony\Bundle\ApiPlatformBundle::class => ['all' => true],
|
|
||||||
Jbtronics\TranslationEditorBundle\JbtronicsTranslationEditorBundle::class => ['dev' => true],
|
|
||||||
];
|
];
|
||||||
|
|
|
@ -1,41 +0,0 @@
|
||||||
api_platform:
|
|
||||||
title: 'Part-DB API'
|
|
||||||
description: 'API of Part-DB'
|
|
||||||
version: '0.1.0'
|
|
||||||
|
|
||||||
formats:
|
|
||||||
jsonld: ['application/ld+json']
|
|
||||||
json: ['application/json']
|
|
||||||
jsonapi: ['application/vnd.api+json']
|
|
||||||
|
|
||||||
docs_formats:
|
|
||||||
jsonld: ['application/ld+json']
|
|
||||||
jsonopenapi: ['application/vnd.openapi+json']
|
|
||||||
html: ['text/html']
|
|
||||||
json: ['application/vnd.openapi+json']
|
|
||||||
|
|
||||||
swagger:
|
|
||||||
api_keys:
|
|
||||||
# overridden in OpenApiFactoryDecorator
|
|
||||||
access_token:
|
|
||||||
name: Authorization
|
|
||||||
type: header
|
|
||||||
|
|
||||||
defaults:
|
|
||||||
# TODO: Change this to true later. In the moment it is false, because we use the session in somewhere
|
|
||||||
stateless: false
|
|
||||||
cache_headers:
|
|
||||||
vary: ['Content-Type', 'Authorization', 'Origin']
|
|
||||||
extra_properties:
|
|
||||||
standard_put: true
|
|
||||||
rfc_7807_compliant_errors: true
|
|
||||||
|
|
||||||
pagination_client_items_per_page: true # Allow clients to override the default items per page
|
|
||||||
|
|
||||||
keep_legacy_inflector: false
|
|
||||||
# Need to be true, or some tests will fail
|
|
||||||
use_symfony_listeners: true
|
|
||||||
|
|
||||||
serializer:
|
|
||||||
# Change this to false later, to remove the hydra prefix on the API
|
|
||||||
hydra_prefix: true
|
|
|
@ -1,5 +0,0 @@
|
||||||
when@test:
|
|
||||||
dama_doctrine_test:
|
|
||||||
enable_static_connection: true
|
|
||||||
enable_static_meta_data_cache: true
|
|
||||||
enable_static_query_cache: true
|
|
|
@ -8,14 +8,15 @@ datatables:
|
||||||
|
|
||||||
# Set options, as documented at https://datatables.net/reference/option/
|
# Set options, as documented at https://datatables.net/reference/option/
|
||||||
options:
|
options:
|
||||||
lengthMenu : [[10, 25, 50, 100], [10, 25, 50, 100]] # We add the "All" option, when part tables are generated
|
lengthMenu : [[10, 25, 50, 100, -1], [10, 25, 50, 100, "All"]]
|
||||||
pageLength: '%partdb.table.default_page_size%' # Set to -1 to disable pagination (i.e. show all rows) by default
|
pageLength: '%partdb.table.default_page_size%' # Set to -1 to disable pagination (i.e. show all rows) by default
|
||||||
dom: " <'row' <'col mb-2 input-group flex-nowrap' B l > <'col-auto mb-2' < p >>>
|
#dom: "<'row' <'col-sm-12' tr>><'row' <'col-sm-6'l><'col-sm-6 text-right'pif>>"
|
||||||
<'card'
|
dom: " <'row'<'col mb-2 input-group' B l> <'col mb-2' <'pull-end' p>>>
|
||||||
rt
|
<'card'
|
||||||
<'card-footer card-footer-table text-muted' i >
|
rt
|
||||||
>
|
<'card-footer card-footer-table text-muted' i >
|
||||||
<'row' <'col mt-2 input-group flex-nowrap' B l > <'col-auto mt-2' < p >>>"
|
>
|
||||||
|
<'row'<'col mt-2 input-group' B l> <'col mt-2' <'pull-right' p>>>"
|
||||||
pagingType: 'simple_numbers'
|
pagingType: 'simple_numbers'
|
||||||
searching: true
|
searching: true
|
||||||
stateSave: true
|
stateSave: true
|
||||||
|
|
5
config/packages/dev/php_translation.yaml
Normal file
5
config/packages/dev/php_translation.yaml
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
translation:
|
||||||
|
symfony_profiler:
|
||||||
|
enabled: true
|
||||||
|
webui:
|
||||||
|
enabled: true
|
|
@ -2,32 +2,18 @@ doctrine:
|
||||||
dbal:
|
dbal:
|
||||||
url: '%env(resolve:DATABASE_URL)%'
|
url: '%env(resolve:DATABASE_URL)%'
|
||||||
|
|
||||||
# Required for DAMA doctrine test bundle
|
|
||||||
use_savepoints: true
|
|
||||||
|
|
||||||
# IMPORTANT: You MUST configure your server version,
|
# IMPORTANT: You MUST configure your server version,
|
||||||
# either here or in the DATABASE_URL env var (see .env file)
|
# either here or in the DATABASE_URL env var (see .env file)
|
||||||
|
|
||||||
types:
|
types:
|
||||||
# UTC datetimes
|
|
||||||
datetime:
|
datetime:
|
||||||
class: App\Doctrine\Types\UTCDateTimeType
|
class: App\Doctrine\Types\UTCDateTimeType
|
||||||
date:
|
date:
|
||||||
class: App\Doctrine\Types\UTCDateTimeType
|
class: App\Doctrine\Types\UTCDateTimeType
|
||||||
|
|
||||||
datetime_immutable:
|
|
||||||
class: App\Doctrine\Types\UTCDateTimeImmutableType
|
|
||||||
date_immutable:
|
|
||||||
class: App\Doctrine\Types\UTCDateTimeImmutableType
|
|
||||||
|
|
||||||
big_decimal:
|
big_decimal:
|
||||||
class: App\Doctrine\Types\BigDecimalType
|
class: App\Doctrine\Types\BigDecimalType
|
||||||
tinyint:
|
tinyint:
|
||||||
class: App\Doctrine\Types\TinyIntType
|
class: App\Doctrine\Types\TinyIntType
|
||||||
|
|
||||||
# This was removed in doctrine/orm 4.0 but we need it for the WebauthnKey entity
|
|
||||||
array:
|
|
||||||
class: App\Doctrine\Types\ArrayType
|
|
||||||
|
|
||||||
schema_filter: ~^(?!internal)~
|
schema_filter: ~^(?!internal)~
|
||||||
# Only enable this when needed
|
# Only enable this when needed
|
||||||
|
@ -40,24 +26,20 @@ doctrine:
|
||||||
validate_xml_mapping: true
|
validate_xml_mapping: true
|
||||||
naming_strategy: doctrine.orm.naming_strategy.underscore_number_aware
|
naming_strategy: doctrine.orm.naming_strategy.underscore_number_aware
|
||||||
auto_mapping: true
|
auto_mapping: true
|
||||||
controller_resolver:
|
|
||||||
auto_mapping: true
|
|
||||||
mappings:
|
mappings:
|
||||||
App:
|
App:
|
||||||
type: attribute
|
|
||||||
is_bundle: false
|
is_bundle: false
|
||||||
|
type: attribute
|
||||||
dir: '%kernel.project_dir%/src/Entity'
|
dir: '%kernel.project_dir%/src/Entity'
|
||||||
prefix: 'App\Entity'
|
prefix: 'App\Entity'
|
||||||
alias: App
|
alias: App
|
||||||
|
|
||||||
dql:
|
dql:
|
||||||
string_functions:
|
string_functions:
|
||||||
regexp: App\Doctrine\Functions\Regexp
|
regexp: DoctrineExtensions\Query\Mysql\Regexp
|
||||||
|
ifnull: DoctrineExtensions\Query\Mysql\IfNull
|
||||||
field: DoctrineExtensions\Query\Mysql\Field
|
field: DoctrineExtensions\Query\Mysql\Field
|
||||||
field2: App\Doctrine\Functions\Field2
|
field2: App\Doctrine\Functions\Field2
|
||||||
natsort: App\Doctrine\Functions\Natsort
|
|
||||||
array_position: App\Doctrine\Functions\ArrayPosition
|
|
||||||
ilike: App\Doctrine\Functions\ILike
|
|
||||||
|
|
||||||
when@test:
|
when@test:
|
||||||
doctrine:
|
doctrine:
|
||||||
|
|
|
@ -2,10 +2,9 @@
|
||||||
framework:
|
framework:
|
||||||
secret: '%env(APP_SECRET)%'
|
secret: '%env(APP_SECRET)%'
|
||||||
csrf_protection: true
|
csrf_protection: true
|
||||||
annotations: false
|
|
||||||
handle_all_throwables: true
|
handle_all_throwables: true
|
||||||
|
|
||||||
# We set this header by ourselves, so we can disable it here
|
# We set this header by ourself, so we can disable it here
|
||||||
disallow_search_engine_index: false
|
disallow_search_engine_index: false
|
||||||
|
|
||||||
# Must be set to true, to enable the change of HTTP method via _method parameter, otherwise our delete routines does not work anymore
|
# Must be set to true, to enable the change of HTTP method via _method parameter, otherwise our delete routines does not work anymore
|
||||||
|
@ -27,6 +26,7 @@ framework:
|
||||||
handler_id: null
|
handler_id: null
|
||||||
cookie_secure: auto
|
cookie_secure: auto
|
||||||
cookie_samesite: lax
|
cookie_samesite: lax
|
||||||
|
storage_factory_id: session.storage.factory.native
|
||||||
|
|
||||||
#esi: true
|
#esi: true
|
||||||
#fragments: true
|
#fragments: true
|
||||||
|
|
|
@ -50,6 +50,7 @@ when@prod:
|
||||||
type: stream
|
type: stream
|
||||||
path: "%kernel.logs_dir%/%kernel.environment%.log"
|
path: "%kernel.logs_dir%/%kernel.environment%.log"
|
||||||
level: debug
|
level: debug
|
||||||
|
formatter: monolog.formatter.json
|
||||||
console:
|
console:
|
||||||
type: console
|
type: console
|
||||||
process_psr_3_messages: false
|
process_psr_3_messages: false
|
||||||
|
@ -73,6 +74,7 @@ when@docker:
|
||||||
type: stream
|
type: stream
|
||||||
path: "php://stderr"
|
path: "php://stderr"
|
||||||
level: debug
|
level: debug
|
||||||
|
formatter: monolog.formatter.json
|
||||||
console:
|
console:
|
||||||
type: console
|
type: console
|
||||||
process_psr_3_messages: false
|
process_psr_3_messages: false
|
||||||
|
|
|
@ -5,7 +5,6 @@ parameters:
|
||||||
saml.sp.privateKey: '%env(string:SAML_SP_PRIVATE_KEY)%'
|
saml.sp.privateKey: '%env(string:SAML_SP_PRIVATE_KEY)%'
|
||||||
|
|
||||||
nbgrp_onelogin_saml:
|
nbgrp_onelogin_saml:
|
||||||
use_proxy_vars: '%env(bool:SAML_BEHIND_PROXY)%'
|
|
||||||
onelogin_settings:
|
onelogin_settings:
|
||||||
default:
|
default:
|
||||||
# Basic settings
|
# Basic settings
|
||||||
|
@ -32,7 +31,7 @@ nbgrp_onelogin_saml:
|
||||||
privateKey: '%env(string:default:saml.sp.privateKey:string:SAMLP_SP_PRIVATE_KEY)%'
|
privateKey: '%env(string:default:saml.sp.privateKey:string:SAMLP_SP_PRIVATE_KEY)%'
|
||||||
|
|
||||||
# Optional settings
|
# Optional settings
|
||||||
baseurl: '%partdb.default_uri%saml/'
|
#baseurl: 'http://myapp.com'
|
||||||
strict: true
|
strict: true
|
||||||
debug: false
|
debug: false
|
||||||
security:
|
security:
|
||||||
|
|
|
@ -1,10 +0,0 @@
|
||||||
nelmio_cors:
|
|
||||||
defaults:
|
|
||||||
origin_regex: true
|
|
||||||
allow_origin: ['%env(CORS_ALLOW_ORIGIN)%']
|
|
||||||
allow_methods: ['GET', 'OPTIONS', 'POST', 'PUT', 'PATCH', 'DELETE']
|
|
||||||
allow_headers: ['Content-Type', 'Authorization']
|
|
||||||
expose_headers: ['Link']
|
|
||||||
max_age: 3600
|
|
||||||
paths:
|
|
||||||
'^/': null
|
|
|
@ -51,16 +51,12 @@ nelmio_security:
|
||||||
img-src:
|
img-src:
|
||||||
- '*'
|
- '*'
|
||||||
- 'data:'
|
- 'data:'
|
||||||
# Required for be able to load pictures in the QR code scanner
|
|
||||||
- 'blob:'
|
|
||||||
style-src:
|
style-src:
|
||||||
- 'self'
|
- 'self'
|
||||||
- 'unsafe-inline'
|
- 'unsafe-inline'
|
||||||
- 'data:'
|
- 'data:'
|
||||||
script-src:
|
script-src:
|
||||||
- 'self'
|
- 'self'
|
||||||
# Required for loading the Wasm for the barcode scanner:
|
|
||||||
- 'wasm-unsafe-eval'
|
|
||||||
object-src:
|
object-src:
|
||||||
- 'self'
|
- 'self'
|
||||||
- 'data:'
|
- 'data:'
|
||||||
|
|
11
config/packages/php_translation.yaml
Normal file
11
config/packages/php_translation.yaml
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
translation:
|
||||||
|
locales: ["en", "de"]
|
||||||
|
edit_in_place:
|
||||||
|
enabled: false
|
||||||
|
config_name: app
|
||||||
|
configs:
|
||||||
|
app:
|
||||||
|
dirs: ["%kernel.project_dir%/templates", "%kernel.project_dir%/src"]
|
||||||
|
output_dir: "%kernel.project_dir%/translations"
|
||||||
|
excluded_names: ["*TestCase.php", "*Test.php"]
|
||||||
|
excluded_dirs: [cache, data, logs]
|
|
@ -6,7 +6,7 @@ scheb_two_factor:
|
||||||
server_name: '$$DOMAIN$$' # This field is replaced by the domain name of the server in DecoratedGoogleAuthenticator
|
server_name: '$$DOMAIN$$' # This field is replaced by the domain name of the server in DecoratedGoogleAuthenticator
|
||||||
issuer: '%partdb.title%' # Issuer name used in QR code
|
issuer: '%partdb.title%' # Issuer name used in QR code
|
||||||
digits: 6 # Number of digits in authentication code
|
digits: 6 # Number of digits in authentication code
|
||||||
leeway: 5 # Acceptable time drift in seconds
|
window: 1 # How many codes before/after the current one would be accepted as valid
|
||||||
template: security/2fa_form.html.twig
|
template: security/2fa_form.html.twig
|
||||||
|
|
||||||
backup_codes:
|
backup_codes:
|
||||||
|
|
|
@ -19,14 +19,11 @@ security:
|
||||||
provider: app_user_provider
|
provider: app_user_provider
|
||||||
lazy: true
|
lazy: true
|
||||||
user_checker: App\Security\UserChecker
|
user_checker: App\Security\UserChecker
|
||||||
entry_point: App\Security\AuthenticationEntryPoint
|
entry_point: form_login
|
||||||
|
|
||||||
# Enable user impersonation
|
# Enable user impersonation
|
||||||
switch_user: { role: CAN_SWITCH_USER }
|
switch_user: { role: CAN_SWITCH_USER }
|
||||||
|
|
||||||
custom_authenticators:
|
|
||||||
- App\Security\ApiTokenAuthenticator
|
|
||||||
|
|
||||||
two_factor:
|
two_factor:
|
||||||
auth_form_path: 2fa_login
|
auth_form_path: 2fa_login
|
||||||
check_path: 2fa_login_check
|
check_path: 2fa_login_check
|
||||||
|
@ -69,7 +66,3 @@ security:
|
||||||
# We get into trouble with the U2F authentication, if the calls to the trees trigger an 2FA login
|
# We get into trouble with the U2F authentication, if the calls to the trees trigger an 2FA login
|
||||||
# This settings should not do much harm, because a read only access to show available data structures is not really critical
|
# This settings should not do much harm, because a read only access to show available data structures is not really critical
|
||||||
- { path: "^/\\w{2}/tree", role: PUBLIC_ACCESS }
|
- { path: "^/\\w{2}/tree", role: PUBLIC_ACCESS }
|
||||||
# Restrict access to API to users, which has the API access permission
|
|
||||||
- { path: "^/api", allow_if: 'is_granted("@api.access_api") and is_authenticated()' }
|
|
||||||
# Restrict access to KICAD to users, which has API access permission
|
|
||||||
- { path: "^/kicad-api", allow_if: 'is_granted("@api.access_api") and is_authenticated()' }
|
|
||||||
|
|
|
@ -20,7 +20,6 @@ twig:
|
||||||
avatar_helper: '@App\Services\UserSystem\UserAvatarHelper'
|
avatar_helper: '@App\Services\UserSystem\UserAvatarHelper'
|
||||||
available_themes: '%partdb.available_themes%'
|
available_themes: '%partdb.available_themes%'
|
||||||
saml_enabled: '%partdb.saml.enabled%'
|
saml_enabled: '%partdb.saml.enabled%'
|
||||||
part_preview_generator: '@App\Services\Attachments\PartPreviewGenerator'
|
|
||||||
|
|
||||||
when@test:
|
when@test:
|
||||||
twig:
|
twig:
|
||||||
|
|
|
@ -11,13 +11,11 @@ parameters:
|
||||||
partdb.banner: '%env(trim:string:BANNER)%' # The info text shown in the homepage, if empty config/banner.md is used
|
partdb.banner: '%env(trim:string:BANNER)%' # The info text shown in the homepage, if empty config/banner.md is used
|
||||||
partdb.default_currency: '%env(string:BASE_CURRENCY)%' # The currency that is used inside the DB (and is assumed when no currency is set). This can not be changed later, so be sure to set it the currency used in your country
|
partdb.default_currency: '%env(string:BASE_CURRENCY)%' # The currency that is used inside the DB (and is assumed when no currency is set). This can not be changed later, so be sure to set it the currency used in your country
|
||||||
partdb.global_theme: '' # The theme to use globally (see public/build/themes/ for choices, use name without .css). Set to '' for default bootstrap theme
|
partdb.global_theme: '' # The theme to use globally (see public/build/themes/ for choices, use name without .css). Set to '' for default bootstrap theme
|
||||||
partdb.locale_menu: ['en', 'de', 'it', 'fr', 'ru', 'ja', 'cs', 'da', 'zh', 'pl'] # The languages that are shown in user drop down menu
|
partdb.locale_menu: ['en', 'de', 'it', 'fr', 'ru', 'ja'] # The languages that are shown in user drop down menu
|
||||||
partdb.enforce_change_comments_for: '%env(csv:ENFORCE_CHANGE_COMMENTS_FOR)%' # The actions for which a change comment is required (e.g. "part_edit", "part_create", etc.). If this is empty, change comments are not required at all.
|
partdb.enforce_change_comments_for: '%env(csv:ENFORCE_CHANGE_COMMENTS_FOR)%' # The actions for which a change comment is required (e.g. "part_edit", "part_create", etc.). If this is empty, change comments are not required at all.
|
||||||
|
|
||||||
partdb.default_uri: '%env(string:DEFAULT_URI)%' # The default URI to use for the Part-DB instance (e.g. https://part-db.example.com/). This is used for generating links in emails
|
partdb.default_uri: '%env(string:DEFAULT_URI)%' # The default URI to use for the Part-DB instance (e.g. https://part-db.example.com/). This is used for generating links in emails
|
||||||
|
|
||||||
partdb.db.emulate_natural_sort: '%env(bool:DATABASE_EMULATE_NATURAL_SORT)%' # If this is set to true, natural sorting is emulated on platforms that do not support it natively. This can be slow on large datasets.
|
|
||||||
|
|
||||||
######################################################################################################################
|
######################################################################################################################
|
||||||
# Users and Privacy
|
# Users and Privacy
|
||||||
######################################################################################################################
|
######################################################################################################################
|
||||||
|
@ -37,7 +35,6 @@ parameters:
|
||||||
# Attachments and files
|
# Attachments and files
|
||||||
######################################################################################################################
|
######################################################################################################################
|
||||||
partdb.attachments.allow_downloads: '%env(bool:ALLOW_ATTACHMENT_DOWNLOADS)%' # Allow users to download attachments to server. Warning: This can be dangerous, because via that feature attackers maybe can access ressources on your intranet!
|
partdb.attachments.allow_downloads: '%env(bool:ALLOW_ATTACHMENT_DOWNLOADS)%' # Allow users to download attachments to server. Warning: This can be dangerous, because via that feature attackers maybe can access ressources on your intranet!
|
||||||
partdb.attachments.download_by_default: '%env(bool:ATTACHMENT_DOWNLOAD_BY_DEFAULT)%' # If this is set the 'download external files' checkbox is set by default for new attachments (only if allow_downloads is set to true)
|
|
||||||
partdb.attachments.dir.media: 'public/media/' # The folder where uploaded attachment files are saved (must be in public folder)
|
partdb.attachments.dir.media: 'public/media/' # The folder where uploaded attachment files are saved (must be in public folder)
|
||||||
partdb.attachments.dir.secure: 'uploads/' # The folder where secured attachment files are saved (must not be in public/)
|
partdb.attachments.dir.secure: 'uploads/' # The folder where secured attachment files are saved (must not be in public/)
|
||||||
partdb.attachments.max_file_size: '%env(string:MAX_ATTACHMENT_FILE_SIZE)%' # The maximum size of an attachment file (in bytes, you can use M for megabytes and G for gigabytes)
|
partdb.attachments.max_file_size: '%env(string:MAX_ATTACHMENT_FILE_SIZE)%' # The maximum size of an attachment file (in bytes, you can use M for megabytes and G for gigabytes)
|
||||||
|
@ -56,8 +53,7 @@ parameters:
|
||||||
######################################################################################################################
|
######################################################################################################################
|
||||||
# Table settings
|
# Table settings
|
||||||
######################################################################################################################
|
######################################################################################################################
|
||||||
partdb.table.default_page_size: '%env(int:TABLE_DEFAULT_PAGE_SIZE)%' # The default number of entries shown per page in tables
|
partdb.table.default_page_size: '%env(int:TABLE_DEFAULT_PAGE_SIZE)%' # The default number of entries shown per page in tables
|
||||||
partdb.table.parts.default_columns: '%env(trim:string:TABLE_PARTS_DEFAULT_COLUMNS)%' # The default columns in part tables and their order
|
|
||||||
|
|
||||||
######################################################################################################################
|
######################################################################################################################
|
||||||
# Sidebar
|
# Sidebar
|
||||||
|
@ -117,8 +113,6 @@ parameters:
|
||||||
env(USE_GRAVATAR): '0'
|
env(USE_GRAVATAR): '0'
|
||||||
env(MAX_ATTACHMENT_FILE_SIZE): '100M'
|
env(MAX_ATTACHMENT_FILE_SIZE): '100M'
|
||||||
|
|
||||||
env(REDIRECT_TO_HTTPS): 0
|
|
||||||
|
|
||||||
env(ENFORCE_CHANGE_COMMENTS_FOR): ''
|
env(ENFORCE_CHANGE_COMMENTS_FOR): ''
|
||||||
|
|
||||||
env(ERROR_PAGE_ADMIN_EMAIL): ''
|
env(ERROR_PAGE_ADMIN_EMAIL): ''
|
||||||
|
@ -146,6 +140,3 @@ parameters:
|
||||||
env(HISTORY_SAVE_REMOVED_DATA): 1
|
env(HISTORY_SAVE_REMOVED_DATA): 1
|
||||||
env(HISTORY_SAVE_NEW_DATA): 1
|
env(HISTORY_SAVE_NEW_DATA): 1
|
||||||
|
|
||||||
env(EDA_KICAD_CATEGORY_DEPTH): 0
|
|
||||||
|
|
||||||
env(DATABASE_EMULATE_NATURAL_SORT): 0
|
|
||||||
|
|
|
@ -25,35 +25,27 @@ perms: # Here comes a list with all Permission names (they have a perm_[name] co
|
||||||
# If a part can be read by a user, he can also see all the datastructures (except devices)
|
# If a part can be read by a user, he can also see all the datastructures (except devices)
|
||||||
alsoSet: ['storelocations.read', 'footprints.read', 'categories.read', 'suppliers.read', 'manufacturers.read',
|
alsoSet: ['storelocations.read', 'footprints.read', 'categories.read', 'suppliers.read', 'manufacturers.read',
|
||||||
'currencies.read', 'attachment_types.read', 'measurement_units.read']
|
'currencies.read', 'attachment_types.read', 'measurement_units.read']
|
||||||
apiTokenRole: ROLE_API_READ_ONLY
|
|
||||||
edit:
|
edit:
|
||||||
label: "perm.edit"
|
label: "perm.edit"
|
||||||
alsoSet: ['read', 'parts_stock.withdraw', 'parts_stock.add', 'parts_stock.move']
|
alsoSet: ['read', 'parts_stock.withdraw', 'parts_stock.add', 'parts_stock.move']
|
||||||
apiTokenRole: ROLE_API_EDIT
|
|
||||||
create:
|
create:
|
||||||
label: "perm.create"
|
label: "perm.create"
|
||||||
alsoSet: ['read', 'edit']
|
alsoSet: ['read', 'edit']
|
||||||
apiTokenRole: ROLE_API_EDIT
|
|
||||||
delete:
|
delete:
|
||||||
label: "perm.delete"
|
label: "perm.delete"
|
||||||
alsoSet: ['read', 'edit']
|
alsoSet: ['read', 'edit']
|
||||||
apiTokenRole: ROLE_API_EDIT
|
|
||||||
change_favorite:
|
change_favorite:
|
||||||
label: "perm.part.change_favorite"
|
label: "perm.part.change_favorite"
|
||||||
alsoSet: ['edit']
|
alsoSet: ['edit']
|
||||||
apiTokenRole: ROLE_API_EDIT
|
|
||||||
show_history:
|
show_history:
|
||||||
label: "perm.part.show_history"
|
label: "perm.part.show_history"
|
||||||
alsoSet: ['read']
|
alsoSet: ['read']
|
||||||
apiTokenRole: ROLE_API_READ_ONLY
|
|
||||||
revert_element:
|
revert_element:
|
||||||
label: "perm.revert_elements"
|
label: "perm.revert_elements"
|
||||||
alsoSet: ["read", "edit", "create", "delete", "show_history"]
|
alsoSet: ["read", "edit", "create", "delete", "show_history"]
|
||||||
apiTokenRole: ROLE_API_EDIT
|
|
||||||
import:
|
import:
|
||||||
label: "perm.import"
|
label: "perm.import"
|
||||||
alsoSet: ["read", "edit", "create"]
|
alsoSet: ["read", "edit", "create"]
|
||||||
apiTokenRole: ROLE_API_EDIT
|
|
||||||
|
|
||||||
parts_stock:
|
parts_stock:
|
||||||
group: "data"
|
group: "data"
|
||||||
|
@ -61,13 +53,10 @@ perms: # Here comes a list with all Permission names (they have a perm_[name] co
|
||||||
operations:
|
operations:
|
||||||
withdraw:
|
withdraw:
|
||||||
label: "perm.parts_stock.withdraw"
|
label: "perm.parts_stock.withdraw"
|
||||||
apiTokenRole: ROLE_API_EDIT
|
|
||||||
add:
|
add:
|
||||||
label: "perm.parts_stock.add"
|
label: "perm.parts_stock.add"
|
||||||
apiTokenRole: ROLE_API_EDIT
|
|
||||||
move:
|
move:
|
||||||
label: "perm.parts_stock.move"
|
label: "perm.parts_stock.move"
|
||||||
apiTokenRole: ROLE_API_EDIT
|
|
||||||
|
|
||||||
|
|
||||||
storelocations: &PART_CONTAINING
|
storelocations: &PART_CONTAINING
|
||||||
|
@ -76,30 +65,23 @@ perms: # Here comes a list with all Permission names (they have a perm_[name] co
|
||||||
operations:
|
operations:
|
||||||
read:
|
read:
|
||||||
label: "perm.read"
|
label: "perm.read"
|
||||||
apiTokenRole: ROLE_API_READ_ONLY
|
|
||||||
edit:
|
edit:
|
||||||
label: "perm.edit"
|
label: "perm.edit"
|
||||||
alsoSet: 'read'
|
alsoSet: 'read'
|
||||||
apiTokenRole: ROLE_API_EDIT
|
|
||||||
create:
|
create:
|
||||||
label: "perm.create"
|
label: "perm.create"
|
||||||
alsoSet: ['read', 'edit']
|
alsoSet: ['read', 'edit']
|
||||||
apiTokenRole: ROLE_API_EDIT
|
|
||||||
delete:
|
delete:
|
||||||
label: "perm.delete"
|
label: "perm.delete"
|
||||||
alsoSet: ['read', 'edit']
|
alsoSet: ['read', 'edit']
|
||||||
apiTokenRole: ROLE_API_EDIT
|
|
||||||
show_history:
|
show_history:
|
||||||
label: "perm.show_history"
|
label: "perm.show_history"
|
||||||
apiTokenRole: ROLE_API_READ_ONLY
|
|
||||||
revert_element:
|
revert_element:
|
||||||
label: "perm.revert_elements"
|
label: "perm.revert_elements"
|
||||||
alsoSet: ["read", "edit", "create", "delete", "show_history"]
|
alsoSet: ["read", "edit", "create", "delete", "show_history"]
|
||||||
apiTokenRole: ROLE_API_EDIT
|
|
||||||
import:
|
import:
|
||||||
label: "perm.import"
|
label: "perm.import"
|
||||||
alsoSet: [ "read", "edit", "create" ]
|
alsoSet: [ "read", "edit", "create" ]
|
||||||
apiTokenRole: ROLE_API_EDIT
|
|
||||||
|
|
||||||
footprints:
|
footprints:
|
||||||
<<: *PART_CONTAINING
|
<<: *PART_CONTAINING
|
||||||
|
@ -163,7 +145,6 @@ perms: # Here comes a list with all Permission names (they have a perm_[name] co
|
||||||
create_parts:
|
create_parts:
|
||||||
label: "perm.part.info_providers.create_parts"
|
label: "perm.part.info_providers.create_parts"
|
||||||
alsoSet: ['parts.create']
|
alsoSet: ['parts.create']
|
||||||
apiTokenRole: ROLE_API_EDIT
|
|
||||||
|
|
||||||
groups:
|
groups:
|
||||||
label: "perm.groups"
|
label: "perm.groups"
|
||||||
|
@ -171,34 +152,26 @@ perms: # Here comes a list with all Permission names (they have a perm_[name] co
|
||||||
operations:
|
operations:
|
||||||
read:
|
read:
|
||||||
label: "perm.read"
|
label: "perm.read"
|
||||||
apiTokenRole: ROLE_API_ADMIN
|
|
||||||
edit:
|
edit:
|
||||||
label: "perm.edit"
|
label: "perm.edit"
|
||||||
alsoSet: 'read'
|
alsoSet: 'read'
|
||||||
apiTokenRole: ROLE_API_ADMIN
|
|
||||||
create:
|
create:
|
||||||
label: "perm.create"
|
label: "perm.create"
|
||||||
alsoSet: ['read', 'edit']
|
alsoSet: ['read', 'edit']
|
||||||
apiTokenRole: ROLE_API_ADMIN
|
|
||||||
delete:
|
delete:
|
||||||
label: "perm.delete"
|
label: "perm.delete"
|
||||||
alsoSet: ['read', 'delete']
|
alsoSet: ['read', 'delete']
|
||||||
apiTokenRole: ROLE_API_ADMIN
|
|
||||||
edit_permissions:
|
edit_permissions:
|
||||||
label: "perm.edit_permissions"
|
label: "perm.edit_permissions"
|
||||||
alsoSet: ['read', 'edit']
|
alsoSet: ['read', 'edit']
|
||||||
apiTokenRole: ROLE_API_ADMIN
|
|
||||||
show_history:
|
show_history:
|
||||||
label: "perm.show_history"
|
label: "perm.show_history"
|
||||||
apiTokenRole: ROLE_API_ADMIN
|
|
||||||
revert_element:
|
revert_element:
|
||||||
label: "perm.revert_elements"
|
label: "perm.revert_elements"
|
||||||
alsoSet: ["read", "edit", "create", "delete", "edit_permissions", "show_history"]
|
alsoSet: ["read", "edit", "create", "delete", "edit_permissions", "show_history"]
|
||||||
apiTokenRole: ROLE_API_ADMIN
|
|
||||||
import:
|
import:
|
||||||
label: "perm.import"
|
label: "perm.import"
|
||||||
alsoSet: [ "read", "edit", "create" ]
|
alsoSet: [ "read", "edit", "create" ]
|
||||||
apiTokenRole: ROLE_API_ADMIN
|
|
||||||
|
|
||||||
users:
|
users:
|
||||||
label: "perm.users"
|
label: "perm.users"
|
||||||
|
@ -206,49 +179,37 @@ perms: # Here comes a list with all Permission names (they have a perm_[name] co
|
||||||
operations:
|
operations:
|
||||||
read:
|
read:
|
||||||
label: "perm.read"
|
label: "perm.read"
|
||||||
apiTokenRole: ROLE_API_ADMIN
|
|
||||||
create:
|
create:
|
||||||
label: "perm.create"
|
label: "perm.create"
|
||||||
alsoSet: ['read', 'edit_username', 'edit_infos']
|
alsoSet: ['read', 'edit_username', 'edit_infos']
|
||||||
apiTokenRole: ROLE_API_ADMIN
|
|
||||||
delete:
|
delete:
|
||||||
label: "perm.delete"
|
label: "perm.delete"
|
||||||
alsoSet: ['read', 'edit_username', 'edit_infos']
|
alsoSet: ['read', 'edit_username', 'edit_infos']
|
||||||
apiTokenRole: ROLE_API_ADMIN
|
|
||||||
edit_username:
|
edit_username:
|
||||||
label: "perm.users.edit_user_name"
|
label: "perm.users.edit_user_name"
|
||||||
alsoSet: ['read']
|
alsoSet: ['read']
|
||||||
apiTokenRole: ROLE_API_ADMIN
|
|
||||||
edit_infos:
|
edit_infos:
|
||||||
label: "perm.users.edit_infos"
|
label: "perm.users.edit_infos"
|
||||||
alsoSet: 'read'
|
alsoSet: 'read'
|
||||||
apiTokenRole: ROLE_API_ADMIN
|
|
||||||
edit_permissions:
|
edit_permissions:
|
||||||
label: "perm.users.edit_permissions"
|
label: "perm.users.edit_permissions"
|
||||||
alsoSet: 'read'
|
alsoSet: 'read'
|
||||||
apiTokenRole: ROLE_API_ADMIN
|
|
||||||
set_password:
|
set_password:
|
||||||
label: "perm.users.set_password"
|
label: "perm.users.set_password"
|
||||||
alsoSet: 'read'
|
alsoSet: 'read'
|
||||||
apiTokenRole: ROLE_API_FULL
|
|
||||||
impersonate:
|
impersonate:
|
||||||
label: "perm.users.impersonate"
|
label: "perm.users.impersonate"
|
||||||
alsoSet: ['set_password']
|
alsoSet: ['set_password']
|
||||||
apiTokenRole: ROLE_API_FULL
|
|
||||||
change_user_settings:
|
change_user_settings:
|
||||||
label: "perm.users.change_user_settings"
|
label: "perm.users.change_user_settings"
|
||||||
apiTokenRole: ROLE_API_ADMIN
|
|
||||||
show_history:
|
show_history:
|
||||||
label: "perm.show_history"
|
label: "perm.show_history"
|
||||||
apiTokenRole: ROLE_API_ADMIN
|
|
||||||
revert_element:
|
revert_element:
|
||||||
label: "perm.revert_elements"
|
label: "perm.revert_elements"
|
||||||
alsoSet: ["read", "create", "delete", "edit_permissions", "show_history", "edit_infos", "edit_username"]
|
alsoSet: ["read", "create", "delete", "edit_permissions", "show_history", "edit_infos", "edit_username"]
|
||||||
apiTokenRole: ROLE_API_ADMIN
|
|
||||||
import:
|
import:
|
||||||
label: "perm.import"
|
label: "perm.import"
|
||||||
alsoSet: [ "read", "create" ]
|
alsoSet: [ "read", "create" ]
|
||||||
apiTokenRole: ROLE_API_ADMIN
|
|
||||||
|
|
||||||
#database:
|
#database:
|
||||||
# label: "perm.database"
|
# label: "perm.database"
|
||||||
|
@ -283,94 +244,64 @@ perms: # Here comes a list with all Permission names (they have a perm_[name] co
|
||||||
operations:
|
operations:
|
||||||
show_logs:
|
show_logs:
|
||||||
label: "perm.show_logs"
|
label: "perm.show_logs"
|
||||||
apiTokenRole: ROLE_API_ADMIN
|
|
||||||
delete_logs:
|
delete_logs:
|
||||||
label: "perm.delete_logs"
|
label: "perm.delete_logs"
|
||||||
alsoSet: 'show_logs'
|
alsoSet: 'show_logs'
|
||||||
apiTokenRole: ROLE_API_ADMIN
|
|
||||||
server_infos:
|
server_infos:
|
||||||
label: "perm.server_infos"
|
label: "perm.server_infos"
|
||||||
apiTokenRole: ROLE_API_ADMIN
|
|
||||||
manage_oauth_tokens:
|
manage_oauth_tokens:
|
||||||
label: "Manage OAuth tokens"
|
label: "Manage OAuth tokens"
|
||||||
apiTokenRole: ROLE_API_ADMIN
|
|
||||||
show_updates:
|
show_updates:
|
||||||
label: "perm.system.show_available_updates"
|
label: "perm.system.show_available_updates"
|
||||||
apiTokenRole: ROLE_API_ADMIN
|
|
||||||
|
|
||||||
|
|
||||||
attachments:
|
attachments:
|
||||||
label: "perm.part.attachments"
|
label: "perm.part.attachments"
|
||||||
operations:
|
operations:
|
||||||
show_private:
|
show_private:
|
||||||
label: "perm.attachments.show_private"
|
label: "perm.attachments.show_private"
|
||||||
apiTokenRole: ROLE_API_READ_ONLY
|
|
||||||
list_attachments:
|
list_attachments:
|
||||||
label: "perm.attachments.list_attachments"
|
label: "perm.attachments.list_attachments"
|
||||||
alsoSet: ['attachment_types.read']
|
alsoSet: ['attachment_types.read']
|
||||||
apiTokenRole: ROLE_API_READ_ONLY
|
|
||||||
|
|
||||||
self:
|
self:
|
||||||
label: "perm.self"
|
label: "perm.self"
|
||||||
operations:
|
operations:
|
||||||
edit_infos:
|
edit_infos:
|
||||||
label: "perm.self.edit_infos"
|
label: "perm.self.edit_infos"
|
||||||
apiTokenRole: ROLE_API_FULL
|
|
||||||
edit_username:
|
edit_username:
|
||||||
label: "perm.self.edit_username"
|
label: "perm.self.edit_username"
|
||||||
apiTokenRole: ROLE_API_FULL
|
|
||||||
show_permissions:
|
show_permissions:
|
||||||
label: "perm.self.show_permissions"
|
label: "perm.self.show_permissions"
|
||||||
apiTokenRole: ROLE_API_READ_ONLY
|
|
||||||
show_logs:
|
show_logs:
|
||||||
label: "perm.self.show_logs"
|
label: "perm.self.show_logs"
|
||||||
apiTokenRole: ROLE_API_FULL
|
|
||||||
|
|
||||||
labels:
|
labels:
|
||||||
label: "perm.labels"
|
label: "perm.labels"
|
||||||
operations:
|
operations:
|
||||||
create_labels:
|
create_labels:
|
||||||
label: "perm.self.create_labels"
|
label: "perm.self.create_labels"
|
||||||
apiTokenRole: ROLE_API_READ_ONLY
|
|
||||||
edit_options:
|
edit_options:
|
||||||
label: "perm.self.edit_options"
|
label: "perm.self.edit_options"
|
||||||
alsoSet: ['create_labels']
|
alsoSet: ['create_labels']
|
||||||
apiTokenRole: ROLE_API_READ_ONLY
|
|
||||||
read_profiles:
|
read_profiles:
|
||||||
label: "perm.self.read_profiles"
|
label: "perm.self.read_profiles"
|
||||||
apiTokenRole: ROLE_API_READ_ONLY
|
|
||||||
edit_profiles:
|
edit_profiles:
|
||||||
label: "perm.self.edit_profiles"
|
label: "perm.self.edit_profiles"
|
||||||
alsoSet: ['read_profiles']
|
alsoSet: ['read_profiles']
|
||||||
apiTokenRole: ROLE_API_EDIT
|
|
||||||
create_profiles:
|
create_profiles:
|
||||||
label: "perm.self.create_profiles"
|
label: "perm.self.create_profiles"
|
||||||
alsoSet: ['read_profiles', 'edit_profiles']
|
alsoSet: ['read_profiles', 'edit_profiles']
|
||||||
apiTokenRole: ROLE_API_EDIT
|
|
||||||
delete_profiles:
|
delete_profiles:
|
||||||
label: "perm.self.delete_profiles"
|
label: "perm.self.delete_profiles"
|
||||||
alsoSet: ['read_profiles', 'edit_profiles', 'create_profiles']
|
alsoSet: ['read_profiles', 'edit_profiles', 'create_profiles']
|
||||||
apiTokenRole: ROLE_API_EDIT
|
|
||||||
use_twig:
|
use_twig:
|
||||||
label: "perm.labels.use_twig"
|
label: "perm.labels.use_twig"
|
||||||
alsoSet: ['create_labels', 'edit_options']
|
alsoSet: ['create_labels', 'edit_options']
|
||||||
apiTokenRole: ROLE_API_ADMIN
|
|
||||||
show_history:
|
show_history:
|
||||||
label: "perm.show_history"
|
label: "perm.show_history"
|
||||||
alsoSet: ['read_profiles']
|
alsoSet: ['read_profiles']
|
||||||
apiTokenRole: ROLE_API_READ_ONLY
|
|
||||||
revert_element:
|
revert_element:
|
||||||
label: "perm.revert_elements"
|
label: "perm.revert_elements"
|
||||||
alsoSet: ['read_profiles', 'edit_profiles', 'create_profiles', 'delete_profiles']
|
alsoSet: ['read_profiles', 'edit_profiles', 'create_profiles', 'delete_profiles']
|
||||||
apiTokenRole: ROLE_API_EDIT
|
|
||||||
|
|
||||||
api:
|
|
||||||
label: "perm.api"
|
|
||||||
operations:
|
|
||||||
access_api:
|
|
||||||
label: "perm.api.access_api"
|
|
||||||
apiTokenRole: ROLE_API_READ_ONLY
|
|
||||||
manage_tokens:
|
|
||||||
label: "perm.api.manage_tokens"
|
|
||||||
alsoSet: ['access_api']
|
|
||||||
apiTokenRole: ROLE_API_FULL
|
|
||||||
|
|
|
@ -15,5 +15,5 @@ redirector:
|
||||||
requirements:
|
requirements:
|
||||||
url: ".*"
|
url: ".*"
|
||||||
controller: App\Controller\RedirectController::addLocalePart
|
controller: App\Controller\RedirectController::addLocalePart
|
||||||
# Dont match localized routes (no redirection loop, if no root with that name exists) or API prefixed routes
|
# Dont match localized routes (no redirection loop, if no root with that name exists)
|
||||||
condition: "not (request.getPathInfo() matches '/^\\\\/([a-z]{2}(_[A-Z]{2})?|api)\\\\//')"
|
condition: "not (request.getPathInfo() matches '/^\\\\/[a-z]{2}(_[A-Z]{2})?\\\\//')"
|
|
@ -1,4 +0,0 @@
|
||||||
api_platform:
|
|
||||||
resource: .
|
|
||||||
type: api_platform
|
|
||||||
prefix: /api
|
|
6
config/routes/dev/php_translation.yaml
Normal file
6
config/routes/dev/php_translation.yaml
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
_translation_webui:
|
||||||
|
resource: '@TranslationBundle/Resources/config/routing_webui.yaml'
|
||||||
|
prefix: /admin
|
||||||
|
|
||||||
|
_translation_profiler:
|
||||||
|
resource: '@TranslationBundle/Resources/config/routing_symfony_profiler.yaml'
|
|
@ -1,3 +0,0 @@
|
||||||
when@dev:
|
|
||||||
translation_editor:
|
|
||||||
resource: '@JbtronicsTranslationEditorBundle/config/routes.php'
|
|
3
config/routes/php_translation.yaml
Normal file
3
config/routes/php_translation.yaml
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
_translation_edit_in_place:
|
||||||
|
resource: '@TranslationBundle/Resources/config/routing_edit_in_place.yaml'
|
||||||
|
prefix: /admin
|
|
@ -1,3 +0,0 @@
|
||||||
_security_logout:
|
|
||||||
resource: security.route_loader.logout
|
|
||||||
type: service
|
|
|
@ -76,18 +76,23 @@ services:
|
||||||
# Only the event classes specified here are saved to DB (set to []) to log all events
|
# Only the event classes specified here are saved to DB (set to []) to log all events
|
||||||
$whitelist: []
|
$whitelist: []
|
||||||
|
|
||||||
App\EventListener\LogSystem\EventLoggerListener:
|
App\EventSubscriber\LogSystem\EventLoggerSubscriber:
|
||||||
arguments:
|
arguments:
|
||||||
$save_changed_fields: '%env(bool:HISTORY_SAVE_CHANGED_FIELDS)%'
|
$save_changed_fields: '%env(bool:HISTORY_SAVE_CHANGED_FIELDS)%'
|
||||||
$save_changed_data: '%env(bool:HISTORY_SAVE_CHANGED_DATA)%'
|
$save_changed_data: '%env(bool:HISTORY_SAVE_CHANGED_DATA)%'
|
||||||
$save_removed_data: '%env(bool:HISTORY_SAVE_REMOVED_DATA)%'
|
$save_removed_data: '%env(bool:HISTORY_SAVE_REMOVED_DATA)%'
|
||||||
$save_new_data: '%env(bool:HISTORY_SAVE_NEW_DATA)%'
|
$save_new_data: '%env(bool:HISTORY_SAVE_NEW_DATA)%'
|
||||||
|
tags:
|
||||||
|
- { name: 'doctrine.event_subscriber' }
|
||||||
|
|
||||||
|
App\EventSubscriber\LogSystem\LogDBMigrationSubscriber:
|
||||||
|
tags:
|
||||||
|
- { name: 'doctrine.event_subscriber' }
|
||||||
|
|
||||||
App\Form\AttachmentFormType:
|
App\Form\AttachmentFormType:
|
||||||
arguments:
|
arguments:
|
||||||
$allow_attachments_download: '%partdb.attachments.allow_downloads%'
|
$allow_attachments_download: '%partdb.attachments.allow_downloads%'
|
||||||
$max_file_size: '%partdb.attachments.max_file_size%'
|
$max_file_size: '%partdb.attachments.max_file_size%'
|
||||||
$download_by_default: '%partdb.attachments.download_by_default%'
|
|
||||||
|
|
||||||
App\Services\Attachments\AttachmentSubmitHandler:
|
App\Services\Attachments\AttachmentSubmitHandler:
|
||||||
arguments:
|
arguments:
|
||||||
|
@ -135,19 +140,6 @@ services:
|
||||||
$saml_role_mapping: '%env(json:SAML_ROLE_MAPPING)%'
|
$saml_role_mapping: '%env(json:SAML_ROLE_MAPPING)%'
|
||||||
$update_group_on_login: '%env(bool:SAML_UPDATE_GROUP_ON_LOGIN)%'
|
$update_group_on_login: '%env(bool:SAML_UPDATE_GROUP_ON_LOGIN)%'
|
||||||
|
|
||||||
|
|
||||||
security.access_token_extractor.header.token:
|
|
||||||
class: Symfony\Component\Security\Http\AccessToken\HeaderAccessTokenExtractor
|
|
||||||
arguments:
|
|
||||||
$tokenType: 'Token'
|
|
||||||
|
|
||||||
security.access_token_extractor.main:
|
|
||||||
class: Symfony\Component\Security\Http\AccessToken\ChainAccessTokenExtractor
|
|
||||||
arguments:
|
|
||||||
$accessTokenExtractors:
|
|
||||||
- '@security.access_token_extractor.header'
|
|
||||||
- '@security.access_token_extractor.header.token'
|
|
||||||
|
|
||||||
####################################################################################################################
|
####################################################################################################################
|
||||||
# Cache
|
# Cache
|
||||||
####################################################################################################################
|
####################################################################################################################
|
||||||
|
@ -219,15 +211,6 @@ services:
|
||||||
arguments:
|
arguments:
|
||||||
$saml_enabled: '%partdb.saml.enabled%'
|
$saml_enabled: '%partdb.saml.enabled%'
|
||||||
|
|
||||||
####################################################################################################################
|
|
||||||
# Table settings
|
|
||||||
####################################################################################################################
|
|
||||||
App\DataTables\PartsDataTable:
|
|
||||||
arguments:
|
|
||||||
$visible_columns: '%partdb.table.parts.default_columns%'
|
|
||||||
|
|
||||||
App\DataTables\Helpers\ColumnSortHelper:
|
|
||||||
shared: false # Service has a state so not share it between different tables
|
|
||||||
|
|
||||||
####################################################################################################################
|
####################################################################################################################
|
||||||
# Label system
|
# Label system
|
||||||
|
@ -294,44 +277,6 @@ services:
|
||||||
$search_limit: '%env(int:PROVIDER_OCTOPART_SEARCH_LIMIT)%'
|
$search_limit: '%env(int:PROVIDER_OCTOPART_SEARCH_LIMIT)%'
|
||||||
$onlyAuthorizedSellers: '%env(bool:PROVIDER_OCTOPART_ONLY_AUTHORIZED_SELLERS)%'
|
$onlyAuthorizedSellers: '%env(bool:PROVIDER_OCTOPART_ONLY_AUTHORIZED_SELLERS)%'
|
||||||
|
|
||||||
App\Services\InfoProviderSystem\Providers\MouserProvider:
|
|
||||||
arguments:
|
|
||||||
$api_key: '%env(string:PROVIDER_MOUSER_KEY)%'
|
|
||||||
$language: '%env(string:PROVIDER_MOUSER_SEARCH_WITH_SIGNUP_LANGUAGE)%'
|
|
||||||
$options: '%env(string:PROVIDER_MOUSER_SEARCH_OPTION)%'
|
|
||||||
$search_limit: '%env(int:PROVIDER_MOUSER_SEARCH_LIMIT)%'
|
|
||||||
|
|
||||||
App\Services\InfoProviderSystem\Providers\LCSCProvider:
|
|
||||||
arguments:
|
|
||||||
$enabled: '%env(bool:PROVIDER_LCSC_ENABLED)%'
|
|
||||||
$currency: '%env(string:PROVIDER_LCSC_CURRENCY)%'
|
|
||||||
|
|
||||||
App\Services\InfoProviderSystem\Providers\OEMSecretsProvider:
|
|
||||||
arguments:
|
|
||||||
$api_key: '%env(string:PROVIDER_OEMSECRETS_KEY)%'
|
|
||||||
$country_code: '%env(string:PROVIDER_OEMSECRETS_COUNTRY_CODE)%'
|
|
||||||
$currency: '%env(PROVIDER_OEMSECRETS_CURRENCY)%'
|
|
||||||
$zero_price: '%env(PROVIDER_OEMSECRETS_ZERO_PRICE)%'
|
|
||||||
$set_param: '%env(PROVIDER_OEMSECRETS_SET_PARAM)%'
|
|
||||||
$sort_criteria: '%env(PROVIDER_OEMSECRETS_SORT_CRITERIA)%'
|
|
||||||
|
|
||||||
|
|
||||||
####################################################################################################################
|
|
||||||
# API system
|
|
||||||
####################################################################################################################
|
|
||||||
App\State\PartDBInfoProvider:
|
|
||||||
arguments:
|
|
||||||
$default_uri: '%partdb.default_uri%'
|
|
||||||
$global_locale: '%partdb.locale%'
|
|
||||||
$global_timezone: '%partdb.timezone%'
|
|
||||||
|
|
||||||
####################################################################################################################
|
|
||||||
# EDA system
|
|
||||||
####################################################################################################################
|
|
||||||
App\Services\EDA\KiCadHelper:
|
|
||||||
arguments:
|
|
||||||
$category_depth: '%env(int:EDA_KICAD_CATEGORY_DEPTH)%'
|
|
||||||
|
|
||||||
####################################################################################################################
|
####################################################################################################################
|
||||||
# Symfony overrides
|
# Symfony overrides
|
||||||
####################################################################################################################
|
####################################################################################################################
|
||||||
|
@ -374,16 +319,6 @@ services:
|
||||||
arguments:
|
arguments:
|
||||||
$check_for_updates: '%partdb.check_for_updates%'
|
$check_for_updates: '%partdb.check_for_updates%'
|
||||||
|
|
||||||
App\Services\System\BannerHelper:
|
|
||||||
arguments:
|
|
||||||
$partdb_banner: '%partdb.banner%'
|
|
||||||
$project_dir: '%kernel.project_dir%'
|
|
||||||
|
|
||||||
App\Doctrine\Middleware\MySQLSSLConnectionMiddlewareWrapper:
|
|
||||||
arguments:
|
|
||||||
$enabled: '%env(bool:DATABASE_MYSQL_USE_SSL_CA)%'
|
|
||||||
$verify: '%env(bool:DATABASE_MYSQL_SSL_VERIFY_CERT)%'
|
|
||||||
|
|
||||||
####################################################################################################################
|
####################################################################################################################
|
||||||
# Monolog
|
# Monolog
|
||||||
####################################################################################################################
|
####################################################################################################################
|
||||||
|
@ -410,4 +345,4 @@ when@test:
|
||||||
arguments:
|
arguments:
|
||||||
- '@doctrine.fixtures.loader'
|
- '@doctrine.fixtures.loader'
|
||||||
- '@doctrine'
|
- '@doctrine'
|
||||||
- { default: '@App\Doctrine\Purger\DoNotUsePurgerFactory' }
|
- { default: '@App\Doctrine\Purger\ResetAutoIncrementPurgerFactory' }
|
|
@ -1,77 +0,0 @@
|
||||||
---
|
|
||||||
title: Authentication
|
|
||||||
layout: default
|
|
||||||
parent: API
|
|
||||||
nav_order: 2
|
|
||||||
---
|
|
||||||
|
|
||||||
# Authentication
|
|
||||||
|
|
||||||
To use API endpoints, the external application has to authenticate itself, so that Part-DB knows which user is accessing
|
|
||||||
the data and which permissions
|
|
||||||
the application should have during the access. Authentication is always bound to a specific user, so the external
|
|
||||||
applications is acting on behalf of a
|
|
||||||
specific user. This user limits the permissions of the application so that it can only access data, which the user is
|
|
||||||
allowed to access.
|
|
||||||
|
|
||||||
The only method currently available for authentication is to use API tokens:
|
|
||||||
|
|
||||||
## API tokens
|
|
||||||
|
|
||||||
An API token is a long alphanumeric string, which is bound to a specific user and can be used to authenticate as this user when accessing the API.
|
|
||||||
The API token is passed via the `Authorization` HTTP header during the API request, like the
|
|
||||||
following: `Authorization: Bearer tcp_sdjfks....`.
|
|
||||||
|
|
||||||
{: .important }
|
|
||||||
> Everybody who knows the API token can access the API as the user, which is bound to the token. So you should treat the
|
|
||||||
> API token like a password
|
|
||||||
> and keep it secret. Only share it with trusted applications.
|
|
||||||
|
|
||||||
API tokens can be created and managed on the user settings page in the API token section. You can create as many API
|
|
||||||
tokens as you want and also delete them again.
|
|
||||||
When deleting a token, it is immediately invalidated and can not be used anymore, which means that the application can
|
|
||||||
not access the API anymore with this token.
|
|
||||||
|
|
||||||
### Token permissions and scopes
|
|
||||||
|
|
||||||
API tokens are ultimately limited by the permissions of the user, which belongs to the token. That means that the token
|
|
||||||
can only access data, that the user is allowed to access, no matter the token permissions.
|
|
||||||
|
|
||||||
But you can further limit the permissions of a token by choosing a specific scope for the token. The scope defines which
|
|
||||||
subset of permissions the token has, which can be less than the permissions of the user. For example, you can have a
|
|
||||||
user
|
|
||||||
with full read and write permissions, but create a token with only read permissions, which can only read data, but not
|
|
||||||
change anything in the database.
|
|
||||||
|
|
||||||
{: .warning }
|
|
||||||
> In general, you should always use the least possible permissions for a token, to limit the possible damage, which can
|
|
||||||
> be done with a stolen token or a bug in the application.
|
|
||||||
> Only use the full or admin scope, if you really need it, as they could potentially be used to do a lot of damage to
|
|
||||||
> your Part-DB instance.
|
|
||||||
|
|
||||||
The following token scopes are available:
|
|
||||||
|
|
||||||
* **Read-Only**: The token can only read non-sensitive data (like parts, but no users or groups) from the API and can
|
|
||||||
not change anything.
|
|
||||||
* **Edit**: The token can read and write non-sensitive data via the API. This includes creating, updating and deleting
|
|
||||||
data. This should be enough for most applications.
|
|
||||||
* **Admin**: The token can read and write all data via the API, including sensitive data like users and groups. This
|
|
||||||
should only be used for trusted applications, which need to access sensitive data and perform administrative actions.
|
|
||||||
* **Full**: The token can do anything the user can do, including changing the user's password and creating new tokens. This
|
|
||||||
should only be used for highly trusted applications!!
|
|
||||||
|
|
||||||
Please note, that in early versions of the API, there might be no endpoints yet, to really perform the actions, which
|
|
||||||
would be allowed by the token scope.
|
|
||||||
|
|
||||||
### Expiration date
|
|
||||||
|
|
||||||
API tokens can have an expiration date, which means that the token is only valid until the expiration date. After that
|
|
||||||
the token is automatically invalidated and can not be used anymore. The token is still listed on the user settings page,
|
|
||||||
and can be deleted there, but the code can not be used to access Part-DB anymore after the expiration date.
|
|
||||||
|
|
||||||
### Get token information
|
|
||||||
|
|
||||||
When authenticating with an API token, you can get information about the currently used token by accessing
|
|
||||||
the `/api/tokens/current` endpoint.
|
|
||||||
It gives you information about the token scope, expiration date and the user, which is bound to the token and the last
|
|
||||||
time the token was used.
|
|
|
@ -1,11 +0,0 @@
|
||||||
---
|
|
||||||
layout: default
|
|
||||||
title: API
|
|
||||||
nav_order: 7
|
|
||||||
has_children: true
|
|
||||||
---
|
|
||||||
|
|
||||||
# API
|
|
||||||
|
|
||||||
Part-DB provides a REST API to access the data stored in the database.
|
|
||||||
In this section you can find information about the API and how to use it.
|
|
|
@ -1,229 +0,0 @@
|
||||||
---
|
|
||||||
title: Introduction
|
|
||||||
layout: default
|
|
||||||
parent: API
|
|
||||||
nav_order: 1
|
|
||||||
---
|
|
||||||
|
|
||||||
# Introduction
|
|
||||||
|
|
||||||
Part-DB provides a [REST API](https://en.wikipedia.org/wiki/REST) to programmatically access the data stored in the
|
|
||||||
database.
|
|
||||||
This allows external applications to interact with Part-DB, extend it or integrate it into other applications.
|
|
||||||
|
|
||||||
{: .warning }
|
|
||||||
> This feature is currently in beta. Please report any bugs you find.
|
|
||||||
> The API should not be considered stable yet and could change in future versions, without prior notice.
|
|
||||||
> Some features might be missing or not working yet.
|
|
||||||
> Also be aware, that there might be security issues in the API, which could allow attackers to access or edit data via
|
|
||||||
> the API, which
|
|
||||||
> they normally should be able to access. So currently you should only use the API with trusted users and trusted
|
|
||||||
> applications.
|
|
||||||
|
|
||||||
Part-DB uses [API Platform](https://api-platform.com/) to provide the API, which allows for easy creation of REST APIs
|
|
||||||
with Symfony and gives you a lot of features out of the box.
|
|
||||||
See the [API Platform documentation](https://api-platform.com/docs/core/) for more details about the API Platform
|
|
||||||
features and how to use them.
|
|
||||||
|
|
||||||
## Enable the API
|
|
||||||
|
|
||||||
The API is available under the `/api` path, but not reachable without proper permissions.
|
|
||||||
You have to give the users, which should be able to access the API the proper permissions (Miscellaneous -> API).
|
|
||||||
Please note that there are two relevant permissions, the first one allows users to access the `/api/` path at all and show the documentation,
|
|
||||||
and the second one allows them to create API tokens which are needed for the authentication of external applications.
|
|
||||||
|
|
||||||
## Authentication
|
|
||||||
|
|
||||||
To use API endpoints, the external application has to authenticate itself, so that Part-DB knows which user is accessing
|
|
||||||
the data and
|
|
||||||
which permissions the application should have. Basically, this is done by creating an API token for a user and then
|
|
||||||
passing it on every request
|
|
||||||
with the `Authorization` header as bearer token, so you add a header `Authorization: Bearer <your token>`.
|
|
||||||
|
|
||||||
See [Authentication chapter]({% link api/authentication.md %}) for more details.
|
|
||||||
|
|
||||||
## API endpoints
|
|
||||||
|
|
||||||
The API is split into different endpoints, which are reachable under the `/api/` path of your Part-DB instance (
|
|
||||||
e.g. `https://your-part-db.local/api/`).
|
|
||||||
There are various endpoints for each entity type (like `part`, `manufacturer`, etc.), which allow you to read and write data, and some special endpoints like `search` or `statistics`.
|
|
||||||
|
|
||||||
For example, all API endpoints for managing categories are available under `/api/categories/`. Depending on the exact
|
|
||||||
path and the HTTP method used, you can read, create, update or delete categories.
|
|
||||||
For most entities, there are endpoints like this:
|
|
||||||
|
|
||||||
* **GET**: `/api/categories/` - List all categories in the database (with pagination of the results)
|
|
||||||
* **POST**: `/api/categories/` - Create a new category
|
|
||||||
* **GET**: `/api/categories/{id}` - Get a specific category by its ID
|
|
||||||
* **DELETE**: `/api/categories/{id}` - Delete a specific category by its ID
|
|
||||||
* **UPDATE**: `/api/categories/{id}` - Update a specific category by its ID. Only the fields which are sent in the
|
|
||||||
request are updated, all other fields are left unchanged.
|
|
||||||
Be aware that you have to set the [JSON Merge Patch](https://datatracker.ietf.org/doc/html/rfc7386) content type
|
|
||||||
header (`Content-Type: application/merge-patch+json`) for this to work.
|
|
||||||
|
|
||||||
A full (interactive) list of endpoints can be displayed when visiting the `/api/` path in your browser, when you are
|
|
||||||
logged in with a user, which is allowed to access the API.
|
|
||||||
There is also a link to this page, on the user settings page in the API token section.
|
|
||||||
This documentation also lists all available fields for each entity type and the allowed operations.
|
|
||||||
|
|
||||||
## Formats
|
|
||||||
|
|
||||||
The API supports different formats for the request and response data, which you can control via the `Accept`
|
|
||||||
and `Content-Type` headers.
|
|
||||||
You should use [JSON-LD](https://json-ld.org/) as format, which is basically JSON with some additional metadata, which
|
|
||||||
allows you to describe the data in a more structured way and also allows to link between different entities. You can achieve this
|
|
||||||
by setting `Accept: application/ld+json` header to the API requests.
|
|
||||||
|
|
||||||
To get plain JSON without any metadata or links, use the `Accept: application/json` header.
|
|
||||||
|
|
||||||
Without an `Accept` header (e.g. when you call the endpoint in a browser), the API will return an HTML page with the
|
|
||||||
documentation, so be sure to include the desired `Accept` header in your API requests.
|
|
||||||
If you can not control the `Accept` header, you can add a `.json` or `.jsonld` suffix to the URL to enforce a JSON or
|
|
||||||
JSON-LD response (e.g. `/api/parts.jsonld`).
|
|
||||||
|
|
||||||
## OpenAPI schema
|
|
||||||
|
|
||||||
Part-DB provides a [OpenAPI](https://swagger.io/specification/) (formally Swagger) schema for the API
|
|
||||||
under `/api/docs.json` (so `https://your-part-db.local/api/docs.json`).
|
|
||||||
This schema is a machine-readable description of the API, which can be imported into software to test the API or even
|
|
||||||
automatically generate client libraries for the API.
|
|
||||||
|
|
||||||
API generators which can generate a client library for the API from the schema are available for many programming
|
|
||||||
languages, like [OpenAPI Generator](https://openapi-generator.tech/).
|
|
||||||
|
|
||||||
An JSONLD/Hydra version of the schema is also available under `/api/docs.jsonld` (
|
|
||||||
so `https://your-part-db.local/api/docs.jsonld`).
|
|
||||||
|
|
||||||
## Interactive documentation
|
|
||||||
|
|
||||||
Part-DB provides an interactive documentation for the API, which is available under `/api/docs` (
|
|
||||||
so `https://your-part-db.local/api/docs`).
|
|
||||||
You can pass your API token in the form on the top of the page, to authenticate yourself, and then you can try out the
|
|
||||||
API directly in the browser.
|
|
||||||
This is a great way to test the API and see how it works, without having to write any code.
|
|
||||||
|
|
||||||
## Pagination
|
|
||||||
|
|
||||||
By default, all list endpoints are paginated, which means only a certain number of results is returned per request.
|
|
||||||
To get another page of the results, you have to use the `page` query parameter, which contains the page number you want
|
|
||||||
to get (e.g. `/api/categoues/?page=2`).
|
|
||||||
When using JSONLD, the links to the next page are also included in the `hydra:view` property of the response.
|
|
||||||
|
|
||||||
To change the size of the pages (the number of items in a single page) use the `itemsPerPage` query parameter (
|
|
||||||
e.g. `/api/categoues/?itemsPerPage=50`).
|
|
||||||
|
|
||||||
See [API Platform docs](https://api-platform.com/docs/core/pagination) for more infos.
|
|
||||||
|
|
||||||
## Filtering results / Searching
|
|
||||||
|
|
||||||
When retrieving a list of entities, you can restrict the results by various filters. Almost all entities have a search
|
|
||||||
filter, which allows you to only include entities, which (text) fields match the given search term: For example, if you only want
|
|
||||||
to get parts, with the Name "BC547", you can use `/api/parts.jsonld?name=BC547`. You can use `%` as a wildcard for multiple
|
|
||||||
characters in the search term (Be sure to properly encode the search term, if you use special characters). For example, if you want
|
|
||||||
to get all parts, whose name starts with "BC", you can use `/api/parts.jsonld?name=BC%25` (the `%25` is the url encoded version of `%`).
|
|
||||||
|
|
||||||
There are other filters available for some entities, allowing you to search on other fields, or restricting the results
|
|
||||||
by numeric values or dates. See the endpoint documentation for the available filters.
|
|
||||||
|
|
||||||
## Filter by associated entities
|
|
||||||
|
|
||||||
To get all parts with a certain category, manufacturer, etc. you can use the `category`, `manufacturer`, etc. query
|
|
||||||
parameters of the `/api/parts` endpoint.
|
|
||||||
They are so-called entity filters and accept a comma-separated list of IDs of the entities you want to filter by.
|
|
||||||
For example, if you want to get all parts with the category "Resistor" (Category ID 1) and "Capacitor" (Category ID 2),
|
|
||||||
you can use `/api/parts.jsonld?category=1,2`.
|
|
||||||
|
|
||||||
Suffix an id with `+` to suffix, to include all direct children categories of the given category. Use the `++` suffix to
|
|
||||||
include all children categories recursively.
|
|
||||||
To get all parts with the category "Resistor" (Category ID 1) and all children categories of "Capacitor" (Category ID
|
|
||||||
2), you can use `/api/parts.jsonld?category=1,2++`.
|
|
||||||
|
|
||||||
See the endpoint documentation for the available entity filters.
|
|
||||||
|
|
||||||
## Ordering results
|
|
||||||
|
|
||||||
When retrieving a list of entities, you can order the results by various fields using the `order` query parameter.
|
|
||||||
For example, if you want to get all parts ordered by their name, you can use `/api/parts/?order[name]=asc`. You can use
|
|
||||||
this parameter multiple times to order by multiple fields.
|
|
||||||
|
|
||||||
See the endpoint documentation for the available fields to order by.
|
|
||||||
|
|
||||||
## Property filter
|
|
||||||
|
|
||||||
Sometimes you only want to get a subset of the properties of an entity, for example when you only need the name of a
|
|
||||||
part, but not all the other properties.
|
|
||||||
You can achieve this using the `properties[]` query parameter with the name of the field you want to get. You can use
|
|
||||||
this parameter multiple times to get multiple fields.
|
|
||||||
For example, if you only want to get the name and the description of a part, you can
|
|
||||||
use `/api/parts/123?properties[]=name&properties[]=description`.
|
|
||||||
It is also possible to use these filters on list endpoints (get collection), to only get a subset of the properties of
|
|
||||||
all entities in the collection.
|
|
||||||
|
|
||||||
See [API Platform docs](https://api-platform.com/docs/core/filters/#property-filter) for more info.
|
|
||||||
|
|
||||||
## Change comment
|
|
||||||
|
|
||||||
Similar to the changes using Part-DB web interface, you can add a change comment to every change you make via the API,
|
|
||||||
which will be
|
|
||||||
visible in the log of the entity.
|
|
||||||
|
|
||||||
You can pass the text for this via the `_comment` query parameter (beware of the proper encoding). For
|
|
||||||
example `/api/parts/123?_comment=This%20is%20a%20change%20comment`.
|
|
||||||
|
|
||||||
## Creating attachments and parameters
|
|
||||||
|
|
||||||
To create attachments and parameters, use the POST endpoint. Internally there are different types of attachments and
|
|
||||||
parameters, for each entity type, where the attachments or parameters are used (e.g. PartAttachment for parts, etc.).
|
|
||||||
The type of the attachment or parameter is automatically determined by the `element` property of the request data if a
|
|
||||||
IRI is passed. You can use the `_type` property to explicitly set the type of the attachment or parameter (the value must
|
|
||||||
be the value of the `@type` property of the owning entity. e.g. `Part` for parts).
|
|
||||||
|
|
||||||
For example, to create an attachment on a part, you can use the following request:
|
|
||||||
|
|
||||||
```
|
|
||||||
POST /api/attachments
|
|
||||||
|
|
||||||
{
|
|
||||||
"name": "front68",
|
|
||||||
"attachment_type": "/api/attachment_types/1",
|
|
||||||
"url": "https://invalid.invalid/test.url",
|
|
||||||
"element": "/api/parts/123"
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Uploading files to attachments
|
|
||||||
|
|
||||||
To upload files to the attachments you can use the special `upload` property of the attachment entity during write operations (POST, PUT, PATCH).
|
|
||||||
Under `data` you can pass a base64 encoded string of the file content, and under `filename` the name of the file.
|
|
||||||
Using the `private` property you can control if the file is the attachment should be stored privately or public.
|
|
||||||
|
|
||||||
For example, to upload a file to an attachment, you can use the following request:
|
|
||||||
|
|
||||||
```
|
|
||||||
PATCH /api/attachments/123
|
|
||||||
|
|
||||||
{
|
|
||||||
"upload": {
|
|
||||||
"data": "data:@file/octet-stream;base64,LS0gcGhwTXlB[...]",
|
|
||||||
"filename": "test.csv",
|
|
||||||
"private": false
|
|
||||||
},
|
|
||||||
"name": "Rename attachment"
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
This also works for creating new attachments, by including the `upload` property in the request data along with the other properties.
|
|
||||||
|
|
||||||
Using the `downloadUrl` property of `upload` you can say Part-DB to upload the file specified at the URL set on the attachment.
|
|
||||||
|
|
||||||
```
|
|
||||||
PATCH /api/attachments/123
|
|
||||||
|
|
||||||
{
|
|
||||||
"upload": {
|
|
||||||
"downloadUrl": true
|
|
||||||
},
|
|
||||||
"url": "https://host.invalid/myfile.pdf"
|
|
||||||
}
|
|
||||||
|
|
||||||
```
|
|
220
docs/concepts.md
220
docs/concepts.md
|
@ -5,85 +5,50 @@ nav_order: 2
|
||||||
---
|
---
|
||||||
|
|
||||||
# Concepts
|
# Concepts
|
||||||
|
|
||||||
This page explains the different concepts of Part-DB and what their intended use is:
|
This page explains the different concepts of Part-DB and what their intended use is:
|
||||||
|
|
||||||
1. TOC
|
1. TOC
|
||||||
{:toc}
|
{:toc}
|
||||||
|
|
||||||
## Part management
|
## Part managment
|
||||||
|
|
||||||
### Part
|
### Part
|
||||||
|
A part is the central concept of Part-DB. A part represents a single kind (or type) of a thing, like an electronic component, an device, an book or similar (depending on what you use Part-DB for). A part entity just represents a certain type of a thing, so if you have 1000 times an BC547 transistor you would create ONE part with the name BC547 and set its quantity to 1000. The individual quantities (so a single BC547 transistor) of a part, should be indistinguishable from each other, so that it does not matter which one of your 1000 things of Part you use.
|
||||||
A part is the central concept of Part-DB. A part represents a single kind (or type) of a thing, like an electronic
|
A part entity have many fields, which can be used to describe it better. Most of the fields are optional:
|
||||||
component, a device, a book or similar (depending on what you use Part-DB for). A part entity just represents a certain
|
* **Name** (Required): The name of the part or how you wanna call it. This could be an manufacturer provided name, or a name you thought of your self. The name have to be unique in a single category.
|
||||||
type of thing, so if you have 1000 times a BC547 transistor you would create ONE part with the name BC547 and set its
|
* **Description**: A short (single-line) description of what this part is/does. For longer informations you should use the comment field or the specifications
|
||||||
quantity to 1000. The individual quantities (so a single BC547 transistor) of a part, should be indistinguishable from
|
|
||||||
each other so that it does not matter which one of your 1000 things of Part you use.
|
|
||||||
A part entity has many fields, which can be used to describe it better. Most of the fields are optional:
|
|
||||||
|
|
||||||
* **Name** (Required): The name of the part or how you want to call it. This could be a manufacturer-provided name, or a
|
|
||||||
name you thought of yourself. Each name needs to be unique and must exist in a single category.
|
|
||||||
* **Description**: A short (single-line) description of what this part is/does. For longer information, you should use
|
|
||||||
the comment field or the specifications
|
|
||||||
* **Category** (Required): The category (see there) to which this part belongs to.
|
* **Category** (Required): The category (see there) to which this part belongs to.
|
||||||
* **Tags**: The list of tags this part belongs to. Tags can be used to group parts logically (similar to the category),
|
* **Tags**: The list of tags this part belong to. Tags can be used to group parts logically (similar to the category), but tags are much less strict and formal (they dont have to be defined forehands) and you can assign multiple tags to a part. When clicking on a tag, a list with all parts which have the same tag, is shown.
|
||||||
but tags are much less strict and formal (they don't have to be defined forehands) and you can assign multiple tags to
|
* **Min Instock**: *Not really implemented yet*. Parts where the total instock is below this value, will show up for ordering.
|
||||||
a part. When clicking on a tag, a list with all parts which have the same tag, is shown.
|
* **Footprint**: See there. Useful especially for electronic parts, which have one of the common electronic footprints (like DIP8, SMD0805 or similar). If a part has no explicit defined preview picture, the preview picture of its footprint will be shown instead in tables.
|
||||||
* **Min Instock**: *Not really implemented yet*. Parts where the total instock is below this value, will show up for
|
|
||||||
ordering.
|
|
||||||
* **Footprint**: See there. Useful especially for electronic parts, which have one of the common electronic footprints (
|
|
||||||
like DIP8, SMD0805 or similar). If a part has no explicitly defined preview picture, the preview picture of its
|
|
||||||
footprint will be shown instead in tables.
|
|
||||||
* **Manufacturer**: The manufacturer which has manufactured (not sold) this part. See Manufacturer entity for more info.
|
* **Manufacturer**: The manufacturer which has manufactured (not sold) this part. See Manufacturer entity for more info.
|
||||||
* **Manufacturer part number** (MPN): If you have used your own name for a part, you can put the part number the
|
* **Manufacturer part number** (MPN): If you have used your own name for a part, you can put the part number the manufacturer uses in this field, so that you can find a part also under its manufacturer number.
|
||||||
manufacturer uses in this field so that you can find a part also under its manufacturer number.
|
* **Link to product page**: If you want to link to the manufacturer website of a part, and it is not possible to determine it automatically from the part name, set in the manufacturer entity (or no manfacturer is set), you can set the link here for each part individually.
|
||||||
* **Link to product page**: If you want to link to the manufacturer website of a part, and it is not possible to
|
* **Manufacturing Status**: The manufacturing status of this part, meaning the information about where the part is in its manufacturing lifecycle.
|
||||||
determine it automatically from the part name, set in the manufacturer entity (or no manufacturer is set), you can set
|
* **Needs review**: If you think parts informations maybe are inaccurate or incomplete and needs some later review/checking, you can set this flag. A part with this flag is marked, so that users know the informations are not completly trustworthy.
|
||||||
the link here for each part individually.
|
|
||||||
* **Manufacturing Status**: The manufacturing status of this part, meaning the information about where the part is in
|
|
||||||
its manufacturing lifecycle.
|
|
||||||
* **Needs review**: If you think parts information may be inaccurate or incomplete and needs some later
|
|
||||||
review/checking, you can set this flag. A part with this flag is marked, so that users know the information is not
|
|
||||||
completely trustworthy.
|
|
||||||
* **Favorite**: Parts with this flag are highlighted in parts lists
|
* **Favorite**: Parts with this flag are highlighted in parts lists
|
||||||
* **Mass**: The mass of a single piece of this part (so of a single transistor). Given in grams.
|
* **Mass**: The mass of a single piece of this part (so of a single transistor). Given in grams.
|
||||||
* **Internal Part number** (IPN): Each part is automatically assigned a numerical ID that identifies a part in the
|
* **Internal Part number** (IPN): Each part is automatically assigned an numerical ID which identifies a part in the database. This ID depends on when a part was created and can not be changed. If you want to assign your own unique identifiers, or sync parts identifiers with the identifiers of another database you can use this field.
|
||||||
database. This ID depends on when a part was created and can not be changed. If you want to assign your own unique
|
|
||||||
identifiers, or sync parts identifiers with the identifiers of another database you can use this field.
|
|
||||||
|
|
||||||
### Stock / Part lot
|
### Stock / Part lot
|
||||||
|
A part can have many stock at multiple different locations. This is represented by part lots / stocks, which consists basically of a storelocation (so where are the parts of this lot are stored) and an amount (how many parts are there).
|
||||||
|
|
||||||
A part can have many stocks at multiple different locations. This is represented by part lots/stocks, which consists
|
### Purchase Informations
|
||||||
basically of a storage location (so where the parts of this lot are stored) and an amount (how many parts are there).
|
The purchase informations describe where the part can be bought (at which vendors) and to which prices.
|
||||||
|
The first part (the order information) describes at which supplier the part can be bought and which is the name of the part under which you can order the part there.
|
||||||
### Purchase Information
|
An order information can contain multiple price informations, which describes the prices for the part at the supplier including bulk discount, etc.
|
||||||
|
|
||||||
The purchase information describes where the part can be bought (at which vendors) and at which prices.
|
|
||||||
The first part (the order information) describes at which supplier the part can be bought and which is the name of the
|
|
||||||
part under which you can order the part there.
|
|
||||||
An order information can contain multiple price information, which describes the prices for the part at the supplier
|
|
||||||
including bulk discount, etc.
|
|
||||||
|
|
||||||
### Parameters
|
### Parameters
|
||||||
|
Parameters represents various specifications / parameters of a part, like the the maximum current of a diode, etc. The advantage of using parameters instead of just putting the data in the comment field or so, is that you can filter for parameters values (including ranges and more) later on.
|
||||||
|
Parameters describe can describe numeric values and/or text values for which they can be filtered. This basically allows you to define custom fields on a part.
|
||||||
|
|
||||||
Parameters represent various specifications/parameters of a part, like the maximum current of a diode, etc. The
|
Using the group field a parameter allows you to group parameters together in the info page later (all parameters with the same group value will be shown under the same group title).
|
||||||
advantage of using parameters instead of just putting the data in the comment field or so, is that you can filter for
|
|
||||||
parameter's values (including ranges and more) later on.
|
|
||||||
Parameters can describe numeric values and/or text values for which they can be filtered. This allows
|
|
||||||
you to define custom fields on a part.
|
|
||||||
|
|
||||||
Using the group field as a parameter allows you to group parameters together on the info page later (all parameters with
|
|
||||||
the same group value will be shown under the same group title).
|
|
||||||
|
|
||||||
## Core data
|
## Core data
|
||||||
|
|
||||||
### Category
|
### Category
|
||||||
|
|
||||||
A category is used to group parts logically by their function (e.g. all NPN transistors would be put in a "
|
A category is used to group parts logically by their function (e.g. all NPN transistors would be put in a "NPN-Transistors" category).
|
||||||
NPN-Transistors" category).
|
Categories are hierarchical structures meaning that you can create logical trees to group categories together. A possible category tree could look like this:
|
||||||
Categories are hierarchical structures meaning that you can create logical trees to group categories together. A
|
|
||||||
possible category tree could look like this:
|
|
||||||
|
|
||||||
* Active Components
|
* Active Components
|
||||||
* Transistors
|
* Transistors
|
||||||
|
@ -95,148 +60,97 @@ possible category tree could look like this:
|
||||||
* MCUs
|
* MCUs
|
||||||
* Passive Components
|
* Passive Components
|
||||||
* Capacitors
|
* Capacitors
|
||||||
* Resistors
|
* Resitors
|
||||||
|
|
||||||
### Supplier
|
### Supplier
|
||||||
|
A Supplier is a vendor / distributor where you can buy/order parts. Price informations of parts are associated with a supplier.
|
||||||
A Supplier is a vendor/distributor where you can buy/order parts. Price information of parts is associated with a
|
|
||||||
supplier.
|
|
||||||
|
|
||||||
### Manufacturer
|
### Manufacturer
|
||||||
|
A manufacturer represents the company that manufacturer / build various parts (not necessary sell them). If the manufacturer also sell the parts, you have to create a supplier for that.
|
||||||
|
|
||||||
A manufacturer represents the company that manufacturers/builds various parts (not necessarily sell them). If the
|
### Storelocation
|
||||||
manufacturer also sells the parts, you have to create a supplier for that.
|
A storelocation represents a place where parts can be stored. This could be a box, a shelf or other things (like the SMD feeder of a machine or so).
|
||||||
|
|
||||||
### Storage location
|
Storelocations are hierarchical to represent storelocations contained in each other.
|
||||||
|
|
||||||
A storage location represents a place where parts can be stored. This could be a box, a shelf, or other things (like the
|
|
||||||
SMD feeder of a machine or so).
|
|
||||||
|
|
||||||
Storage locations are hierarchical to represent storage locations contained in each other.
|
|
||||||
An example tree could look like this:
|
An example tree could look like this:
|
||||||
|
|
||||||
* Shelf 1
|
* Shelf 1
|
||||||
* Box 1
|
* Box 1
|
||||||
* Box 2
|
* Box 2
|
||||||
* Box shelf A1
|
* Box shelf A1
|
||||||
* Box shelf A2
|
* Box shelf A2
|
||||||
* Box shelf B1
|
* Box shelf B1
|
||||||
* Box shelf B2
|
* Box shelf B2
|
||||||
* Shelf 2
|
* Shelf 2
|
||||||
* Cupboard
|
* Cupboard
|
||||||
|
|
||||||
Storage locations should be defined down to the smallest possible location, to make finding the part again easy.
|
Storelocations should be defined down to the smallest possible location, to make finding the part again easy.
|
||||||
|
|
||||||
### Footprint
|
### Footprint
|
||||||
|
In electronics many components have one of the common components cases / footprints. The footprint entity describes such common footprints, which can be assigned to parts.
|
||||||
|
You can assign an image (and an 3D model) as an attachment to a footprint, which will be used as preview for parts with this footprint, even if the parts do not have an explicitly assigned preview image.
|
||||||
|
|
||||||
In electronics, many components have one of the common components cases/footprints. The footprint entity describes such
|
Footprints are a hierachically which allows you to build logical sorted trees. An example tree could look like this:
|
||||||
common footprints, which can be assigned to parts.
|
|
||||||
You can assign an image (and a 3D model) as an attachment to a footprint, which will be used as preview for parts with
|
|
||||||
this footprint, even if the parts do not have an explicitly assigned preview image.
|
|
||||||
|
|
||||||
Footprints are hierarchically which allows you to build logically sorted trees. An example tree could look like this:
|
|
||||||
|
|
||||||
* Through-Hole components
|
* Through-Hole components
|
||||||
* DIP
|
* DIP
|
||||||
* DIP-8
|
* DIP-8
|
||||||
* DIP-28
|
* DIP-28
|
||||||
* DIP-28W
|
* DIP-28W
|
||||||
* TO
|
* TO
|
||||||
* TO-92
|
* TO-92
|
||||||
* SMD components
|
* SMD components
|
||||||
* SOIC
|
* SOIC
|
||||||
* SO-8
|
* SO-8
|
||||||
* Resistors
|
* Resistors
|
||||||
* 0805
|
* 0805
|
||||||
* 0603
|
* 0603
|
||||||
|
|
||||||
### Measurement Unit
|
### Measurement Unit
|
||||||
|
By default part instock is counted in number of individual parts, which is fine for things like electronic components, which exists only in integer quantities. However if you have things with fractional units like the length of a wire or the volume of a liquid, you have to define a measurement unit.
|
||||||
|
The measurement unit represents a physical quantity like mass, volume or length.
|
||||||
|
|
||||||
By default, part in stock is counted in number of individual parts, which is fine for things like electronic components,
|
You can define a short unit for it (like m for Meters, or g for gramms) which will be shown, when a quantity of a part with this unit is shown.
|
||||||
which exist only in integer quantities. However, if you have things with fractional units like the length of a wire or
|
|
||||||
the volume of a liquid, you have to define a measurement unit.
|
|
||||||
The measurement unit represents a physical quantity like mass, volume, or length.
|
|
||||||
|
|
||||||
You can define a short unit for it (like m for Meters, or g for grams) which will be shown when a quantity of a part
|
|
||||||
with this unit is shown.
|
|
||||||
|
|
||||||
In order to cover wider use cases and allow you to define measurement units further, it is possible to define parameters
|
|
||||||
associated to a measurement unit. These parameters are distinct from a part's parameters and are not inherited.
|
|
||||||
|
|
||||||
### Currency
|
### Currency
|
||||||
|
By default all prices are set in the base currency configured for the instance (by default euros). If you want to use multiple currencies together (as e.g. vendors use foreign currencies for their price and you do not want to update the prices for every exchange rate change), you have to define these currencies here.
|
||||||
|
|
||||||
By default, all prices are set in the base currency configured for the instance (by default euros). If you want to use
|
You can set an exchange rate here in terms of the base currency (or fetch it from the internet if configured). The exchange rate will be used to show users the prices in their preferred currency.
|
||||||
multiple currencies together (e.g. vendors use foreign currencies for their price, and you do not want to update the
|
|
||||||
prices for every exchange rate change), you have to define these currencies here.
|
|
||||||
|
|
||||||
You can set an exchange rate here in terms of the base currency (or fetch it from the internet if configured). The
|
|
||||||
exchange rate will be used to show users the prices in their preferred currency.
|
|
||||||
|
|
||||||
## Attachments
|
## Attachments
|
||||||
|
|
||||||
### Attachment
|
### Attachment
|
||||||
|
An attachment is an file that can be associated with another entity (like a Part, Storelocation, User, etc.). This could for example be a datasheet in a Part, the logo of a vendor or some CAD drawing of a footprint.
|
||||||
|
|
||||||
An attachment is a file that can be associated with another entity (like a Part, location, User, etc.). This could
|
An attachment has an attachment type (see below), which groups the attachments logically (and optionally restricts the allowed file types), a name describing the attachment and a file. The file can either be uploaded to the server and stored there, or given as a link to a file on another webpath. If configured in the settings, it is also possible that the webserver downloads the file from the supplied website and stores it locally on the server.
|
||||||
for example be a datasheet in a Part, the logo of a vendor or some CAD drawing of a footprint.
|
|
||||||
|
|
||||||
An attachment has an attachment type (see below), which groups the attachments logically (and optionally restricts the
|
By default all uploaded files, are accessible for everyone (even non logged in users), if the link is known. If your Part-DB instance is publicly available and you want to store private/sensitve files on it, you should mark the attachment as "Private attachment". Private attachments are only accessible to users, which has the permission to access private attachments.
|
||||||
allowed file types), a name describing the attachment and a file. The file can either be uploaded to the server and
|
Please not, that no thumbnails are generated for private attachments, which can have an performance impact.
|
||||||
stored there, or given as a link to a file on another web path. If configured in the settings, it is also possible that
|
|
||||||
the web server downloads the file from the supplied website and stores it locally on the server.
|
|
||||||
|
|
||||||
By default, all uploaded files, are accessible for everyone (even non-logged-in users), if the link is known. If your
|
Part-DB ships some preview images for various common footprints like DIP-8 and others, as internal ressources. These can be accessed/searched by typing the keyword in the URL field of a part and choosing one of the choices from the dropdown.
|
||||||
Part-DB instance is publicly available, and you want to store private/sensitive files on it, you should mark the
|
|
||||||
attachment as "Private attachment". Private attachments are only accessible to users, which has permission to access
|
|
||||||
private attachments.
|
|
||||||
Please note, that no thumbnails are generated for private attachments, which can have a performance impact.
|
|
||||||
|
|
||||||
Part-DB ships some preview images for various common footprints like DIP-8 and others, as internal resources. These can
|
### Preview image / attachment
|
||||||
be accessed/searched by typing the keyword in the URL field of a part and choosing one of the choices from the dropdown.
|
Most entities with attachments allow you to select one of the defined attachments as "Preview image". You can select an image attachment here, that previews the entity, this could be a picture of a Part, the logo of a manufacturer or supplier, the schematic symbol of a category or the image of an footprint.
|
||||||
|
The preview image will be shown in various locations together with the entities name.
|
||||||
|
|
||||||
### Preview image/attachment
|
Please note that as long as the picture is not secret, it should be stored on the Part-DB instance (by upload, or letting Part-DB download the file) and *not* be marked as a private attachments, so that thumbnails can be generated for the picture (which improves performance).
|
||||||
|
|
||||||
Most entities with attachments allow you to select one of the defined attachments as "Preview image". You can select an
|
|
||||||
image attachment here, that previews the entity, this could be a picture of a Part, the logo of a manufacturer or
|
|
||||||
supplier, the schematic symbol of a category or the image of a footprint.
|
|
||||||
The preview image will be shown in various locations together with the entity's name.
|
|
||||||
|
|
||||||
Please note that as long as the picture is not secret, it should be stored on the Part-DB instance (by uploading, or
|
|
||||||
letting Part-DB download the file) and *not* be marked as a private attachment, so that thumbnails can be generated for
|
|
||||||
the picture (which improves performance).
|
|
||||||
|
|
||||||
### Attachment types
|
### Attachment types
|
||||||
|
Attachment types define logical groups of attachments. For example you could define an attachment group "Datasheets" where all datasheets of Parts, Footprints, etc. belong in, "Pictures" for preview images and more.
|
||||||
Attachment types define logical groups of attachments. For example, you could define an attachment group "Datasheets"
|
You can define file type restrictions, which file types and extensions are allowed for files with that attachment type.
|
||||||
where all datasheets of Parts, Footprints, etc. belong in, "Pictures" for preview images and more.
|
|
||||||
You can define file type restrictions, and which file types and extensions are allowed for files with that attachment type.
|
|
||||||
|
|
||||||
## User System
|
## User System
|
||||||
|
|
||||||
### User
|
### User
|
||||||
|
Each person which should be able to use Part-DB (by logging in) is represented by an user entity, which defines things like access rights, the password, and other things. For security reasons, every person which will use Part-DB should use its own personal account with an secret password. This allows to track activity of the users via the log.
|
||||||
|
|
||||||
Each person who should be able to use Part-DB (by logging in) is represented by a user entity, which defines things
|
There is a special user called `anonymous`, whose access rights are used to determine what an non-logged in user can do. Normally the anonymous user should be the most restricted user.
|
||||||
like access rights, the password, and other things. For security reasons, every person who will use Part-DB should use
|
|
||||||
their own personal account with a secret password. This allows to track activity of the users via the log.
|
|
||||||
|
|
||||||
There is a special user called `anonymous`, whose access rights are used to determine what a non-logged-in user can do.
|
|
||||||
Normally the anonymous user should be the most restricted user.
|
|
||||||
|
|
||||||
For simplification of access management users can be assigned to groups.
|
For simplification of access management users can be assigned to groups.
|
||||||
|
|
||||||
### Group
|
### Group
|
||||||
|
A group is entity, to which users can be assigned to. This can be used to logically group users by for example organisational structures and to simplify permissions managment, as you can define groups with access rights for common use cases and then just assign users to them, without the need to change every permission on the users individually.
|
||||||
A group is an entity, to which users can be assigned to. This can be used to logically group users by for example
|
|
||||||
organizational structures and to simplify permissions management, as you can define groups with access rights for common
|
|
||||||
use cases and then just assign users to them, without the need to change every permission on the users individually.
|
|
||||||
|
|
||||||
## Labels
|
## Labels
|
||||||
|
|
||||||
### Label profiles
|
### Label profiles
|
||||||
|
A label profile represents an template for a label (for a storelocation, a part or part lot). It consists of a size, an barcode type and the content. There are various placeholders which can be inserted in the text content and which will be used replaced with data for the actual thing.
|
||||||
|
|
||||||
A label profile represents a template for a label (for a storage location, a part or part lot). It consists of a size, a
|
You do not have to define a label profile to generate labels (you can just set the settings on the fly in the label dialog), however if you want to generate many labels, it is recommended to save the settings as label profile, to save it for later usage. This ensures that all generated labels look the same.
|
||||||
barcode type and the content. There are various placeholders that can be inserted in the text content and which will be
|
|
||||||
replaced with data for the actual thing.
|
|
||||||
|
|
||||||
You do not have to define a label profile to generate labels (you can just set the settings on the fly in the label
|
|
||||||
dialog), however, if you want to generate many labels, it is recommended to save the settings as a label profile, to save
|
|
||||||
it for later usage. This ensures that all generated labels look the same.
|
|
|
@ -6,250 +6,107 @@ nav_order: 5
|
||||||
|
|
||||||
# Configuration
|
# Configuration
|
||||||
|
|
||||||
Part-DBs behavior can be configured to your needs. There are different kinds of configuration options: Options, which are
|
Part-DBs behavior can be configured to your needs. There are different kind of configuration options: Options which are user changable (changable dynamically via frontend), options which can be configured by environment variables, and options which are only configurable via symfony config files.
|
||||||
user-changeable (changeable dynamically via frontend), options that can be configured by environment variables, and
|
|
||||||
options that are only configurable via Symfony config files.
|
|
||||||
|
|
||||||
## User changeable
|
## User changable
|
||||||
|
Following things can be changed for every user and a user can change it for himself (if he has the correct permission for it). Configuration is either possible via the users own setting page (where you can also change the password) or via the user admin page:
|
||||||
The following things can be changed for every user and a user can change it for himself (if he has the correct permission
|
* **Language**: The language that the users prefers, and which will be used when no language is explicitly specified. Language can still always be changed via the language selector. By default the global configured language is used.
|
||||||
for it). Configuration is either possible via the user's own settings page (where you can also change the password) or via
|
* **Timezone**: The timezone which the user resides in and in which all dates and times should be shown. By default the globally configured language.
|
||||||
the user admin page:
|
* **Theme**: The theme to use for the frontend. Allows the user to choose the frontend design, he prefers.
|
||||||
|
* **Prefered currency**: One of the defined currencies, in which all prices should be shown, if possible. Prices with other currencies will be converted to the price selected here
|
||||||
* **Language**: The language that the users prefer, and which will be used when no language is explicitly specified.
|
|
||||||
Language can still always be changed via the language selector. By default, the globally configured language is used.
|
|
||||||
* **Timezone**: The timezone in which the user resides and in which all dates and times should be shown. By default, the
|
|
||||||
globally configured language.
|
|
||||||
* **Theme**: The theme to use for the front end. Allows the user to choose the front end design, he prefers.
|
|
||||||
* **Preferred currency**: One of the defined currencies, in which all prices should be shown, if possible. Prices with
|
|
||||||
other currencies will be converted to the price selected here
|
|
||||||
|
|
||||||
## Environment variables (.env.local)
|
## Environment variables (.env.local)
|
||||||
|
The following configuration options can only be changed by the server administrator, by either changing the server variables, changing the `.env.local` file or setting env for your docker container. Here are just the most important options listed, see `.env` file for full list of possible env variables.
|
||||||
The following configuration options can only be changed by the server administrator, by either changing the server
|
|
||||||
variables, changing the `.env.local` file or setting env for your docker container. Here are just the most important
|
|
||||||
options listed, see `.env` file for the full list of possible env variables.
|
|
||||||
|
|
||||||
### General options
|
### General options
|
||||||
|
* `DATABASE_URL`: Configures the database which Part-DB uses. For mysql use a string in the form of `mysql://<USERNAME>:<PASSWORD>@<HOST>:<PORT>/<TABLE_NAME>` here (e.g. `DATABASE_URL=mysql://user:password@127.0.0.1:3306/part-db`. For sqlite use the following format to specify the absolute path where it should be located `sqlite:///path/part/app.db`. You can use `%kernel.project_dir%` as placeholder for the Part-DB root folder (e.g. `sqlite:///%kernel.project_dir%/var/app.db`)
|
||||||
* `DATABASE_URL`: Configures the database which Part-DB uses:
|
* `DEFAULT_LANG`: The default language to use serverwide (when no language is explictly specified by a user or via language chooser). Must be something like `en`, `de`, `fr`, etc.
|
||||||
* For MySQL (or MariaDB) use a string in the form of `mysql://<USERNAME>:<PASSWORD>@<HOST>:<PORT>/<TABLE_NAME>` here
|
* `DEFAULT_TIMEZONE`: The default timezone to use globally, when a user has not timezone specified. Must be something like `Europe/Berlin`. See [here](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones) under TZ Database name for a list of available options.
|
||||||
(e.g. `DATABASE_URL=mysql://user:password@127.0.0.1:3306/part-db`).
|
* `BASE_CURRENCY`: The currency to use internally for monetary values and when no currency is explictly specified. When migrating from a legacy Part-DB version, this should be the same as the currency in the old Part-DB instance (normally euro). This should be the currency you use the most. **Please note that you can not really change this setting after you have created data**. The value has to be a valid [ISO4217](https://en.wikipedia.org/wiki/ISO_4217) code, like `EUR` or `USD`.
|
||||||
* For SQLite use the following format to specify the
|
* `INSTANCE_NAME`: The name of your installation. It will be shown as a title in the navbar and other places. By default `Part-DB`, but you can customize it to something likes `ExampleCorp. Inventory`.
|
||||||
absolute path where it should be located `sqlite:///path/part/app.db`. You can use `%kernel.project_dir%` as
|
* `ALLOW_ATTACHMENT_DOWNLOADS` (allowed values `0` or `1`): By setting this option to 1, users can make Part-DB directly download a file specified as an URL and create it as local file. Please not that this allows users access to all ressources publicly available to the server (so full access to other servers in the same local network), which could be a security risk.
|
||||||
placeholder for the Part-DB root folder (e.g. `sqlite:///%kernel.project_dir%/var/app.db`)
|
* `USE_GRAVATAR`: Set to `1` to use [gravatar.com](gravatar.com) images for user avatars (as long as they have not set their own picture). The users browsers have to download the pictures from a third-party (gravatars) server, so this might be a privacy risk.
|
||||||
* For Postgresql use a string in the form of `DATABASE_URL=postgresql://user:password@127.0.0.1:5432/part-db?serverVersion=x.y`.
|
* `MAX_ATTACHMENT_FILE_SIZE`: The maximum file size (in bytes) for attachments. You can use the suffix `K`, `M` or `G` to specify the size in kilobytes, megabytes or gigabytes. By default `100M` (100 megabytes). Please note that this only the limit of Part-DB. You still need to configure the php.ini `upload_max_filesize` and `post_max_size` to allow bigger files to be uploaded.
|
||||||
|
* `DEFAULT_URI`: The default URI base to use for the Part-DB, when no URL can be determined from the browser request. This should be the primary URL/Domain, which is used to access Part-DB. This value is used to create correct links in emails and other places, where the URL is needed. It is also used, when SAML is enabled.s If you are using a reverse proxy, you should set this to the URL of the reverse proxy (e.g. `https://part-db.example.com`). **This value must end with a slash**.
|
||||||
Please note that **`serverVersion=x.y`** variable is required due to dependency of Symfony framework.
|
* `ENFORCE_CHANGE_COMMENTS_FOR`: With this option you can configure, where users are enforced to give a change reason, which will be written to the log. This is a comma separated list of values (e.g. `part_edit,part_delete`). Leave empty to make change comments optional everywhere. Possible values are:
|
||||||
|
* `part_edit`: Edit operation of a existing part
|
||||||
* `DATABASE_MYSQL_USE_SSL_CA`: If this value is set to `1` or `true` and a MySQL connection is used, then the connection
|
* `part_delete`: Delete operation of a existing part
|
||||||
is encrypted by SSL/TLS and the server certificate is verified against the system CA certificates or the CA certificate
|
* `part_create`: Creation of a new part
|
||||||
bundled with Part-DB. Set `DATABASE_MYSQL_SSL_VERIFY_CERT` if you want to accept all certificates.
|
* `part_stock_operation`: Stock operation on a part (therefore withdraw, add or move stock)
|
||||||
* `DATABASE_EMULATE_NATURAL_SORT` (default 0): If set to 1, Part-DB will emulate natural sorting, even if the database
|
* `datastructure_edit`: Edit operation of a existing datastructure (e.g. category, manufacturer, ...)
|
||||||
does not support it natively. However this is much slower than the native sorting, and contain bugs or quirks, so use
|
* `datastructure_delete`: Delete operation of a existing datastructure (e.g. category, manufacturer, ...)
|
||||||
it only, if you have to.
|
* `datastructure_create`: Creation of a new datastructure (e.g. category, manufacturer, ...)
|
||||||
* `DEFAULT_LANG`: The default language to use server-wide (when no language is explicitly specified by a user or via
|
* `CHECK_FOR_UPDATES` (default `1`): Set this to 0, if you do not want Part-DB to connect to GitHub to check for new versions, or if your server can not connect to the internet.
|
||||||
language chooser). Must be something like `en`, `de`, `fr`, etc.
|
|
||||||
* `DEFAULT_TIMEZONE`: The default timezone to use globally, when a user has no timezone specified. Must be something
|
|
||||||
like `Europe/Berlin`. See [here](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones) under TZ Database name
|
|
||||||
for a list of available options.
|
|
||||||
* `BASE_CURRENCY`: The currency to use internally for monetary values and when no currency is explicitly specified. When
|
|
||||||
migrating from a legacy Part-DB version, this should be the same as the currency in the old Part-DB instance (normally
|
|
||||||
euro). This should be the currency you use the most. **Please note that you can not really change this setting after
|
|
||||||
you have created data**. The value has to be a valid [ISO4217](https://en.wikipedia.org/wiki/ISO_4217) code,
|
|
||||||
like `EUR` or `USD`.
|
|
||||||
* `INSTANCE_NAME`: The name of your installation. It will be shown as a title in the navbar and other places. By
|
|
||||||
default `Part-DB`, but you can customize it to something likes `ExampleCorp. Inventory`.
|
|
||||||
* `ALLOW_ATTACHMENT_DOWNLOADS` (allowed values `0` or `1`): By setting this option to 1, users can make Part-DB directly
|
|
||||||
download a file specified as a URL and create it as a local file. Please note that this allows users access to all
|
|
||||||
resources publicly available to the server (so full access to other servers in the same local network), which could
|
|
||||||
be a security risk.
|
|
||||||
* `ATTACHMENT_DOWNLOAD_BY_DEFAULT`: When this is set to 1, the "download external file" checkbox is checked by default
|
|
||||||
when adding a new attachment. Otherwise, it is unchecked by default. Use this if you wanna download all attachments
|
|
||||||
locally by default. Attachment download is only possible, when `ALLOW_ATTACHMENT_DOWNLOADS` is set to 1.
|
|
||||||
* `USE_GRAVATAR`: Set to `1` to use [gravatar.com](https://gravatar.com/) images for user avatars (as long as they have
|
|
||||||
not set their own picture). The users browsers have to download the pictures from a third-party (gravatar) server, so
|
|
||||||
this might be a privacy risk.
|
|
||||||
* `MAX_ATTACHMENT_FILE_SIZE`: The maximum file size (in bytes) for attachments. You can use the suffix `K`, `M` or `G`
|
|
||||||
to specify the size in kilobytes, megabytes or gigabytes. By default `100M` (100 megabytes). Please note that this is
|
|
||||||
only the limit of Part-DB. You still need to configure the php.ini `upload_max_filesize` and `post_max_size` to allow
|
|
||||||
bigger files to be uploaded.
|
|
||||||
* `DEFAULT_URI`: The default URI base to use for the Part-DB, when no URL can be determined from the browser request.
|
|
||||||
This should be the primary URL/Domain, which is used to access Part-DB. This value is used to create correct links in
|
|
||||||
emails and other places, where the URL is needed. It is also used, when SAML is enabled.s If you are using a reverse
|
|
||||||
proxy, you should set this to the URL of the reverse proxy (e.g. `https://part-db.example.com`). **This value must end
|
|
||||||
with a slash**.
|
|
||||||
* `ENFORCE_CHANGE_COMMENTS_FOR`: With this option, you can configure, where users are enforced to give a change reason,
|
|
||||||
which will be written to the log. This is a comma-separated list of values (e.g. `part_edit,part_delete`). Leave empty
|
|
||||||
to make change comments optional everywhere. Possible values are:
|
|
||||||
* `part_edit`: Edit operation of an existing part
|
|
||||||
* `part_delete`: Delete operation of an existing part
|
|
||||||
* `part_create`: Creation of a new part
|
|
||||||
* `part_stock_operation`: Stock operation on a part (therefore withdraw, add or move stock)
|
|
||||||
* `datastructure_edit`: Edit operation of an existing datastructure (e.g. category, manufacturer, ...)
|
|
||||||
* `datastructure_delete`: Delete operation of a existing datastructure (e.g. category, manufacturer, ...)
|
|
||||||
* `datastructure_create`: Creation of a new datastructure (e.g. category, manufacturer, ...)
|
|
||||||
* `CHECK_FOR_UPDATES` (default `1`): Set this to 0, if you do not want Part-DB to connect to GitHub to check for new
|
|
||||||
versions, or if your server can not connect to the internet.
|
|
||||||
* `APP_SECRET`: This variable is a configuration parameter used for various security-related purposes,
|
|
||||||
particularly for securing and protecting various aspects of your application. It's a secret key that is used for
|
|
||||||
cryptographic operations and security measures (session management, CSRF protection, etc..). Therefore this
|
|
||||||
value should be handled as confidential data and not shared publicly.
|
|
||||||
|
|
||||||
### E-Mail settings
|
### E-Mail settings
|
||||||
|
* `MAILER_DSN`: You can configure the mail provider which should be used for email delivery (see https://symfony.com/doc/current/components/mailer.html for full documentation). If you just want to use an SMTP mail account, you can use the following syntax `MAILER_DSN=smtp://user:password@smtp.mailserver.invalid:587`
|
||||||
* `MAILER_DSN`: You can configure the mail provider which should be used for email delivery (
|
* `EMAIL_SENDER_EMAIL`: The email address from which emails should be sent from (in most cases this has to be the same as the email address used for SMTP access)
|
||||||
see https://symfony.com/doc/current/components/mailer.html for full documentation). If you just want to use an SMTP
|
* `EMAIL_SENDER_NAME`: Similar to `EMAIL_SENDER_EMAIL` but this allows you to specify the name from which the mails are sent from.
|
||||||
mail account, you can use the following syntax `MAILER_DSN=smtp://user:password@smtp.mailserver.invalid:587`
|
* `ALLOW_EMAIL_PW_RESET`: Set this value to true, if you wan to allow users to reset their password via an email notification. You have to configure the mailprovider first before via the MAILER_DSN setting.
|
||||||
* `EMAIL_SENDER_EMAIL`: The email address from which emails should be sent from (in most cases this has to be the same
|
|
||||||
as the email address used for SMTP access)
|
|
||||||
* `EMAIL_SENDER_NAME`: Similar to `EMAIL_SENDER_EMAIL`, but this allows you to specify the name from which the mails are
|
|
||||||
sent from.
|
|
||||||
* `ALLOW_EMAIL_PW_RESET`: Set this value to true, if you want to allow users to reset their password via an email
|
|
||||||
notification. You have to configure the mail provider first before via the MAILER_DSN setting.
|
|
||||||
|
|
||||||
### Table related settings
|
### Table related settings
|
||||||
|
* `TABLE_DEFAULT_PAGE_SIZE`: The default page size for tables. This is the number of rows which are shown per page. Set to `-1` to disable pagination and show all rows at once.
|
||||||
|
|
||||||
* `TABLE_DEFAULT_PAGE_SIZE`: The default page size for tables. This is the number of rows which are shown per page. Set
|
### History/Eventlog related settings
|
||||||
to `-1` to disable pagination and show all rows at once.
|
|
||||||
* `TABLE_PARTS_DEFAULT_COLUMNS`: The columns in parts tables, which are visible by default (when loading table for first
|
|
||||||
time).
|
|
||||||
Also specify the default order of the columns. This is a comma separated list of column names. Available columns
|
|
||||||
are: `name`, `id`, `ipn`, `description`, `category`, `footprint`, `manufacturer`, `storage_location`, `amount`, `minamount`, `partUnit`, `addedDate`, `lastModified`, `needs_review`, `favorite`, `manufacturing_status`, `manufacturer_product_number`, `mass`, `tags`, `attachments`, `edit`.
|
|
||||||
|
|
||||||
### History/Eventlog-related settings
|
|
||||||
|
|
||||||
The following options are used to configure, which (and how much) data is written to the system log:
|
The following options are used to configure, which (and how much) data is written to the system log:
|
||||||
|
* `HISTORY_SAVE_CHANGED_FIELDS`: When this option is set to true, the name of the fields which are changed, are saved to the DB (so for example it is logged that a user has changed, that the user has changed the name and description of the field, but not the data/content of these changes)
|
||||||
|
* `HISTORY_SAVE_CHANGED_DATA`: When this option is set to true, the changed data is saved to log (so it is logged, that a user has changed the name of a part and what the name was before). This can increase database size, when you have a lot of changes to entities.
|
||||||
|
* `HISTORY_SAVE_REMOVED_DATA`: When this option is set to true, removed data is saved to log, meaning that you can easily undelete an entity, when it was removed accidentally.
|
||||||
|
* `HISTORY_SAVE_NEW_DATA`: When this option is set to true, the new data (the data after a change) is saved to element changed log entries. This allows you to easily see the changes between two revisions of an entity. This can increase database size, when you have a lot of changes to entities.
|
||||||
|
|
||||||
* `HISTORY_SAVE_CHANGED_FIELDS`: When this option is set to true, the name of the fields that are changed, are saved to
|
If you wanna use want to revert changes or view older revisions of entities, then `HISTORY_SAVE_CHANGED_FIELDS`, `HISTORY_SAVE_CHANGED_DATA` and `HISTORY_SAVE_REMOVED_DATA` all have to be true.
|
||||||
the DB (so for example it is logged that a user has changed, that the user has changed the name and description of the
|
|
||||||
field, but not the data/content of these changes)
|
|
||||||
* `HISTORY_SAVE_CHANGED_DATA`: When this option is set to true, the changed data is saved to log (so it is logged, that
|
|
||||||
a user has changed the name of a part and what the name was before). This can increase database size when you have a
|
|
||||||
lot of changes to entities.
|
|
||||||
* `HISTORY_SAVE_REMOVED_DATA`: When this option is set to true, removed data is saved to log, meaning that you can
|
|
||||||
easily undelete an entity, when it was removed accidentally.
|
|
||||||
* `HISTORY_SAVE_NEW_DATA`: When this option is set to true, the new data (the data after a change) is saved to element
|
|
||||||
changed log entries. This allows you to easily see the changes between two revisions of an entity. This can increase
|
|
||||||
database size, when you have a lot of changes to entities.
|
|
||||||
|
|
||||||
If you want to use want to revert changes or view older revisions of entities,
|
|
||||||
then `HISTORY_SAVE_CHANGED_FIELDS`, `HISTORY_SAVE_CHANGED_DATA` and `HISTORY_SAVE_REMOVED_DATA` all have to be true.
|
|
||||||
|
|
||||||
### Error pages settings
|
### Error pages settings
|
||||||
|
* `ERROR_PAGE_ADMIN_EMAIL`: You can set an email-address here, which is shown on the error page, who should be contacted about the issue (e.g. an IT support email of your company)
|
||||||
* `ERROR_PAGE_ADMIN_EMAIL`: You can set an email address here, which is shown on the error page, who should be contacted
|
* `ERROR_PAGE_SHOW_HELP`: Set this 0, to disable the solution hints shown on an error page. These hints should not contain senstive informations, but could confuse end-users.
|
||||||
about the issue (e.g. an IT support email of your company)
|
|
||||||
* `ERROR_PAGE_SHOW_HELP`: Set this 0, to disable the solution hints shown on an error page. These hints should not
|
|
||||||
contain sensitive information but could confuse end-users.
|
|
||||||
|
|
||||||
### EDA related settings
|
|
||||||
|
|
||||||
* `EDA_KICAD_CATEGORY_DEPTH`: A number, which determines how many levels of Part-DB categories should be shown inside KiCad.
|
|
||||||
All parts in the selected category and all subcategories are shown in KiCad.
|
|
||||||
For performance reason this value should not be too high. The default is 0, which means that only the top level categories are shown in KiCad.
|
|
||||||
All parts in the selected category and all subcategories are shown in KiCad. Set this to a higher value, if you want to show more categories in KiCad.
|
|
||||||
When you set this value to -1, all parts are shown inside a single category in KiCad.
|
|
||||||
|
|
||||||
### SAML SSO settings
|
### SAML SSO settings
|
||||||
|
The following settings can be used to enable and configure Single-Sign on via SAML. This allows users to login to Part-DB without entering a username and password, but instead they are redirected to a SAML Identity Provider (IdP) and are logged in automatically. This is especially useful, when you want to use Part-DB in a company, where all users have a SAML account (e.g. via Active Directory or LDAP).
|
||||||
|
You can find more advanced settings in the `config/packages/hslavich_onelogin_saml.yaml` file. Please note that this file is not backuped by the backup script, so you have to backup it manually, if you want to keep your changes. If you want to edit it on docker, you have to map the file to a volume.
|
||||||
|
|
||||||
The following settings can be used to enable and configure Single-Sign on via SAML. This allows users to log in to
|
* `SAML_ENABLED`: When this is set to 1, SAML SSO is enabled and the SSO Login button is shown in the login form. You have to configure the SAML settings below, before you can use this feature.
|
||||||
Part-DB without entering a username and password, but instead they are redirected to a SAML Identity Provider (IdP) and
|
* `SAML_ROLE_MAPPING`: A [JSON](https://en.wikipedia.org/wiki/JSON) encoded map which specifies how Part-DB should convert the user roles given by SAML attribute `group` should be converted to a Part-DB group (specified by ID). You can use a wildcard `*` to map all otherwise unmapped roles to a certain group. Example: `{"*": 1, "admin": 2, "editor": 3}`. This would map all roles to the group with ID 1, except the role `admin`, which is mapped to the group with ID 2 and the role `editor`, which is mapped to the group with ID 3.
|
||||||
are logged in automatically. This is especially useful when you want to use Part-DB in a company, where all users have
|
* `SAML_UPDATE_GROUP_ON_LOGIN`: When this is enabled the group of the user is updated on every login of the user based on the SAML role attributes. When this is disabled, the group is only assigned on the first login of the user, and a Part-DB administrator can change the group afterwards by editing the user.
|
||||||
a SAML account (e.g. via Active Directory or LDAP).
|
* `SAML_IDP_ENTITY_ID`: The entity ID of your SAML Identity Provider (IdP). You can find this value in the metadata XML file or configuration UI of your IdP.
|
||||||
You can find more advanced settings in the `config/packages/hslavich_onelogin_saml.yaml` file. Please note that this
|
* `SAML_IDP_SINGLE_SIGN_ON_SERVICE`: The URL of the SAML IdP Single Sign-On Service (SSO). You can find this value in the metadata XML file or configuration UI of your IdP.
|
||||||
file is not backed up by the backup script, so you have to back up it manually, if you want to keep your changes. If you
|
* `SAML_IDP_SINGLE_LOGOUT_SERVICE`: The URL of the SAML IdP Single Logout Service (SLO). You can find this value in the metadata XML file or configuration UI of your IdP.
|
||||||
want to edit it on docker, you have to map the file to a volume.
|
* `SAML_IDP_X509_CERT`: The base64 encoded X.509 public certificate of your SAML IdP. You can find this value in the metadata XML file or configuration UI of your IdP. It should start with `MIIC` and end with `=`.
|
||||||
|
* `SAML_SP_ENTITY_ID`: The entity ID of your SAML Service Provider (SP). This is the value you have configured for the Part-DB client in your IdP.
|
||||||
* `SAML_ENABLED`: When this is set to 1, SAML SSO is enabled and the SSO Login button is shown in the login form. You
|
* `SAML_SP_X509_CERT`: The public X.509 certificate of your SAML SP (here Part-DB). This is the value you have configured for the Part-DB client in your IdP. It should start with `MIIC` and end with `=`. IdPs like keycloak allows you to generate a public/private key pair for the client which you can setup here and in the `SAML_SP_PRIVATE_KEY` setting.
|
||||||
have to configure the SAML settings below before you can use this feature.
|
* `SAML_SP_PRIVATE_KEY`: The private key of your SAML SP (here Part-DB), corresponding the public key specified in `SAML_SP_X509_CERT`. This is the value you have configured for the Part-DB client in your IdP. It should start with `MIIE` and end with `=`. IdPs like keycloak allows you to generate a public/private key pair for the client which you can setup here and in the `SAML_SP_X509_CERT` setting.
|
||||||
* `SAML_BEHIND_PROXY`: Set this to 1, if Part-DB is behind a reverse proxy. See [here]({% link installation/reverse-proxy.md %})
|
|
||||||
for more information. Otherwise, leave it to 0 (default.)
|
|
||||||
* `SAML_ROLE_MAPPING`: A [JSON](https://en.wikipedia.org/wiki/JSON)-encoded map which specifies how Part-DB should
|
|
||||||
convert the user roles given by SAML attribute `group` should be converted to a Part-DB group (specified by ID). You
|
|
||||||
can use a wildcard `*` to map all otherwise unmapped roles to a certain group.
|
|
||||||
Example: `{"*": 1, "admin": 2, "editor": 3}`. This would map all roles to the group with ID 1, except the
|
|
||||||
role `admin`, which is mapped to the group with ID 2, and the role `editor`, which is mapped to the group with ID 3.
|
|
||||||
* `SAML_UPDATE_GROUP_ON_LOGIN`: When this is enabled the group of the user is updated on every login of the user based
|
|
||||||
on the SAML role attributes. When this is disabled, the group is only assigned on the first login of the user, and a
|
|
||||||
Part-DB administrator can change the group afterward by editing the user.
|
|
||||||
* `SAML_IDP_ENTITY_ID`: The entity ID of your SAML Identity Provider (IdP). You can find this value in the metadata XML
|
|
||||||
file or configuration UI of your IdP.
|
|
||||||
* `SAML_IDP_SINGLE_SIGN_ON_SERVICE`: The URL of the SAML IdP Single Sign-On Service (SSO). You can find this value in
|
|
||||||
the metadata XML file or configuration UI of your IdP.
|
|
||||||
* `SAML_IDP_SINGLE_LOGOUT_SERVICE`: The URL of the SAML IdP Single Logout Service (SLO). You can find this value in the
|
|
||||||
metadata XML file or configuration UI of your IdP.
|
|
||||||
* `SAML_IDP_X509_CERT`: The base64 encoded X.509 public certificate of your SAML IdP. You can find this value in the
|
|
||||||
metadata XML file or configuration UI of your IdP. It should start with `MIIC` and end with `=`.
|
|
||||||
* `SAML_SP_ENTITY_ID`: The entity ID of your SAML Service Provider (SP). This is the value you have configured for the
|
|
||||||
Part-DB client in your IdP.
|
|
||||||
* `SAML_SP_X509_CERT`: The public X.509 certificate of your SAML SP (here Part-DB). This is the value you have
|
|
||||||
configured for the Part-DB client in your IdP. It should start with `MIIC` and end with `=`. IdPs like keycloak allows
|
|
||||||
you to generate a public/private key pair for the client which you can set up here and in the `SAML_SP_PRIVATE_KEY`
|
|
||||||
setting.
|
|
||||||
* `SAML_SP_PRIVATE_KEY`: The private key of your SAML SP (here Part-DB), corresponding the public key specified
|
|
||||||
in `SAML_SP_X509_CERT`. This is the value you have configured for the Part-DB client in your IdP. It should start
|
|
||||||
with `MIIE` and end with `=`. IdPs like keycloak allows you to generate a public/private key pair for the client which
|
|
||||||
you can set up here and in the `SAML_SP_X509_CERT` setting.
|
|
||||||
|
|
||||||
### Information provider settings
|
### Information provider settings
|
||||||
|
|
||||||
The settings prefixes with `PROVIDER_*` are used to configure the information providers.
|
The settings prefixes with `PROVIDER_*` are used to configure the information providers.
|
||||||
See the [information providers]({% link usage/information_provider_system.md %}) page for more information.
|
See the [information providers]({% link usage/information_provider_system.md %}) page for more information.
|
||||||
|
|
||||||
### Other / less-used options
|
### Other / less used options
|
||||||
|
* `TRUSTED_PROXIES`: Set the IP addresses (or IP blocks) of trusted reverse proxies here. This is needed to get correct IP informations (see [here](https://symfony.com/doc/current/deployment/proxies.html) for more info).
|
||||||
* `TRUSTED_PROXIES`: Set the IP addresses (or IP blocks) of trusted reverse proxies here. This is needed to get correct
|
* `TRUSTED_HOSTS`: To prevent `HTTP Host header attacks` you can set a regex containing all host names via which Part-DB should be accessible. If accessed via the wrong hostname, an error will be shown.
|
||||||
IP information (see [here](https://symfony.com/doc/current/deployment/proxies.html) for more info).
|
* `DEMO_MODE`: Set Part-DB into demo mode, which forbids users to change their passwords and settings. Used for the demo instance, should not be needed for normal installations.
|
||||||
* `TRUSTED_HOSTS`: To prevent `HTTP Host header attacks` you can set a regex containing all host names via which Part-DB
|
* `NO_URL_REWRITE_AVAILABLE` (allowed values `true` or `false`): Set this value to true, if your webserver does not support rewrite. In this case, all URL pathes will contain index.php/, which is needed then. Normally this setting do not need to be changed.
|
||||||
should be accessible. If accessed via the wrong hostname, an error will be shown.
|
* `FIXER_API_KEY`: If you want to automatically retrieve exchange rates for base currencies other than euros, you have configure an exchange rate provider API. [Fixer.io](https://fixer.io/) is preconfigured, and you just have to register there and set the retrieved API key in this environment variable.
|
||||||
* `DEMO_MODE`: Set Part-DB into demo mode, which forbids users to change their passwords and settings. Used for the demo
|
* `APP_ENV`: This value should always be set to `prod` in normal use. Set it to `dev` to enable debug/development mode. (**You should not do this on a publicly accessible server, as it will leak sensitive informations!**)
|
||||||
instance. This should not be needed for normal installations.
|
* `BANNER`: You can configure the text that should be shown as the banner on the homepage. Useful especially for docker container. In all other applications you can just change the `config/banner.md` file.
|
||||||
* `NO_URL_REWRITE_AVAILABLE` (allowed values `true` or `false`): Set this value to true, if your webserver does not
|
|
||||||
support rewrite. In this case, all URL paths will contain index.php/, which is needed then. Normally this setting does
|
|
||||||
not need to be changed.
|
|
||||||
* `REDIRECT_TO_HTTPS`: If this is set to true, all requests to http will be redirected to https. This is useful if your
|
|
||||||
web server does not already do this (like the one used in the demo instance). If your web server already redirects to
|
|
||||||
https, you don't need to set this. Ensure that Part-DB is accessible via HTTPS before you enable this setting.
|
|
||||||
* `FIXER_API_KEY`: If you want to automatically retrieve exchange rates for base currencies other than euros, you have to
|
|
||||||
configure an exchange rate provider API. [Fixer.io](https://fixer.io/) is preconfigured, and you just have to register
|
|
||||||
there and set the retrieved API key in this environment variable.
|
|
||||||
* `APP_ENV`: This value should always be set to `prod` in normal use. Set it to `dev` to enable debug/development
|
|
||||||
mode. (**You should not do this on a publicly accessible server, as it will leak sensitive information!**)
|
|
||||||
* `BANNER`: You can configure the text that should be shown as the banner on the homepage. Useful especially for docker
|
|
||||||
containers. In all other applications you can just change the `config/banner.md` file.
|
|
||||||
* `DISABLE_YEAR2038_BUG_CHECK`: If set to `1`, the year 2038 bug check is disabled on 32-bit systems, and dates after
|
|
||||||
2038 are no longer forbidden. However this will lead to 500 error messages when rendering dates after 2038 as all current
|
|
||||||
32-bit PHP versions can not format these dates correctly. This setting is for the case that future PHP versions will
|
|
||||||
handle this correctly on 32-bit systems. 64-bit systems are not affected by this bug, and the check is always disabled.
|
|
||||||
|
|
||||||
## Banner
|
## Banner
|
||||||
|
|
||||||
To change the banner you can find on the homepage, you can either set the `BANNER` environment variable to the text you
|
To change the banner you can find on the homepage, you can either set the `BANNER` environment variable to the text you
|
||||||
want to show, or you can edit the `config/banner.md` file. The banner is written in markdown, so you can use all
|
want to show, or you can edit the `config/banner.md` file. The banner is written in markdown, so you can use all
|
||||||
markdown (and even some subset of HTML) syntax to format the text.
|
markdown (and even some subset of HTML) syntax to format the text.
|
||||||
|
|
||||||
## parameters.yaml
|
## parameters.yaml
|
||||||
|
You can also configure some options via the `config/parameters.yaml` file. This should normally not needed,
|
||||||
|
and you should know what you are doing, when you change something here. You should expect, that you will have to do some
|
||||||
|
manual merge, when you have changed something here and update to a newer version of Part-DB. It is possible that configuration
|
||||||
|
options here will change or completely removed in future versions of Part-DB.
|
||||||
|
|
||||||
You can also configure some options via the `config/parameters.yaml` file. This should normally not need,
|
If you change something here, you have to clear the cache, before the changes will take effect with the command `bin/console cache:clear`.
|
||||||
and you should know what you are doing, when you change something here. You should expect, that you will have to do some
|
|
||||||
manual merge, when you have changed something here and update to a newer version of Part-DB. It is possible that
|
|
||||||
configuration options here will change or be completely removed in future versions of Part-DB.
|
|
||||||
|
|
||||||
If you change something here, you have to clear the cache, before the changes will take effect with the
|
|
||||||
command `bin/console cache:clear`.
|
|
||||||
|
|
||||||
The following options are available:
|
The following options are available:
|
||||||
|
|
||||||
* `partdb.global_theme`: The default theme to use, when no user specific theme is set. Should be one of the themes from
|
* `partdb.global_theme`: The default theme to use, when no user specific theme is set. Should be one of the themes from the `partdb.available_themes` config option.
|
||||||
the `partdb.available_themes` config option.
|
* `partdb.locale_menu`: The codes of the languages, which should be shown in the language chooser menu (the one with the user icon in the navbar). The first language in the list will be the default language.
|
||||||
* `partdb.locale_menu`: The codes of the languages, which should be shown in the language chooser menu (the one with the
|
* `partdb.gdpr_compliance`: When set to true (default value), IP addresses which are saved in the database will be anonymized, by removing the last byte of the IP. This is required by the GDPR (General Data Protection Regulation) in the EU.
|
||||||
user icon in the navbar). The first language in the list will be the default language.
|
* `partdb.sidebar.items`: The panel contents which should be shown in the sidebar by default. You can also change the number of sidebar panels by changing the number of items in this list.
|
||||||
* `partdb.gdpr_compliance`: When set to true (default value), IP addresses which are saved in the database will be
|
|
||||||
anonymized, by removing the last byte of the IP. This is required by the GDPR (General Data Protection Regulation) in
|
|
||||||
the EU.
|
|
||||||
* `partdb.sidebar.items`: The panel contents which should be shown in the sidebar by default. You can also change the
|
|
||||||
number of sidebar panels by changing the number of items in this list.
|
|
||||||
* `partdb.sidebar.root_node_enable`: Show a root node in the sidebar trees, of which all nodes are children of
|
* `partdb.sidebar.root_node_enable`: Show a root node in the sidebar trees, of which all nodes are children of
|
||||||
* `partdb.sidebar.root_expanded`: Expand the root node in the sidebar trees by default
|
* `partdb.sidebar.root_expanded`: Expand the root node in the sidebar trees by default
|
||||||
* `partdb.available_themes`: The list of available themes a user can choose from.
|
* `partdb.available_themes`: The list of available themes a user can choose from.
|
|
@ -5,52 +5,39 @@ nav_order: 0
|
||||||
---
|
---
|
||||||
|
|
||||||
# Part-DB
|
# Part-DB
|
||||||
|
|
||||||
Part-DB is an Open-Source inventory management system for your electronic components.
|
Part-DB is an Open-Source inventory management system for your electronic components.
|
||||||
It is installed on a web server and so can be accessed with any browser without the need to install additional software.
|
It is installed on a web server and so can be accessed with any browser without the need to install additional software.
|
||||||
|
|
||||||
{: .important-title }
|
{: .important-title }
|
||||||
> Demo
|
> Demo
|
||||||
>
|
>
|
||||||
> If you want to test Part-DB without installing it, you can use [this](https://demo.part-db.de/) Heroku instance.
|
> If you want to test Part-DB without installing it, you can use [this](https://part-db.herokuapp.com) Heroku instance.
|
||||||
> (Or this link for the [German Version](https://demo.part-db.de/de/)).
|
> (Or this link for the [German Version](https://part-db.herokuapp.com/de/)).
|
||||||
>
|
>
|
||||||
> You can log in with username: **user** and password: **user**, to change/create data.
|
> You can log in with username: **user** and password: **user**, to change/create data.
|
||||||
>
|
>
|
||||||
> Every change to the master branch gets automatically deployed, so it represents the current development progress and
|
> Every change to the master branch gets automatically deployed, so it represents the currenct development progress and is
|
||||||
> is
|
> maybe not completly stable. Please mind, that the free Heroku instance is used, so it can take some time when loading the page
|
||||||
> maybe not completely stable. Please mind, that the free Heroku instance is used, so it can take some time when loading
|
|
||||||
> the page
|
|
||||||
> for the first time.
|
> for the first time.
|
||||||
|
|
||||||
## Features
|
## Features
|
||||||
|
* Inventory management of your electronic parts. Each part can be assigned to a category, footprint, manufacturer
|
||||||
* Inventory management of your electronic parts. Each part can be assigned to a category, footprint, manufacturer,
|
and multiple store locations and price information. Parts can be grouped using tags. You can associate various files like datasheets or pictures with the parts.
|
||||||
and multiple store locations and price information. Parts can be grouped using tags. You can associate various files
|
* Multi-Language support (currently German, English, Russian, Japanese and French (experimental))
|
||||||
like datasheets or pictures with the parts.
|
|
||||||
* Multi-language support (currently German, English, Russian, Japanese and French (experimental))
|
|
||||||
* Barcodes/Labels generator for parts and storage locations, scan barcodes via webcam using the builtin barcode scanner
|
* Barcodes/Labels generator for parts and storage locations, scan barcodes via webcam using the builtin barcode scanner
|
||||||
* User system with groups and detailed (fine granular) permissions.
|
* User system with groups and detailed (fine granular) permissions.
|
||||||
Two-factor authentication is supported (Google Authenticator and Webauthn/U2F keys) and can be enforced for groups.
|
Two-factor authentication is supported (Google Authenticator and Webauthn/U2F keys) and can be enforced for groups. Password reset via email can be setuped.
|
||||||
Password reset via email can be setup.
|
* Optional support for single sign-on (SSO) via SAML (using an intermediate service like [Keycloak](https://www.keycloak.org/) you can connect Part-DB to an existing LDAP or Active Directory server)
|
||||||
* Optional support for single sign-on (SSO) via SAML (using an intermediate service
|
|
||||||
like [Keycloak](https://www.keycloak.org/) you can connect Part-DB to an existing LDAP or Active Directory server)
|
|
||||||
* Import/Export system
|
* Import/Export system
|
||||||
* Project management: Create projects and assign parts to the bill of material (BOM), to show how often you could build
|
* Project management: Create projects and assign parts to the bill of material (BOM), to show how often you could build this project and directly withdraw all components needed from DB
|
||||||
this project and directly withdraw all components needed from DB
|
* Event log: Track what changes happens to your inventory, track which user does what. Revert your parts to older versions.
|
||||||
* Event log: Track what changes happens to your inventory, track which user does what. Revert your parts to older
|
|
||||||
versions.
|
|
||||||
* Responsive design: You can use Part-DB on your PC, your tablet and your smartphone using the same interface.
|
* Responsive design: You can use Part-DB on your PC, your tablet and your smartphone using the same interface.
|
||||||
* MySQL, SQLite and PostgreSQL are supported as database backends
|
* MySQL and SQLite supported as database backends
|
||||||
* Support for rich text descriptions and comments in parts
|
* Support for rich text descriptions and comments in parts
|
||||||
* Support for multiple currencies and automatic update of exchange rates supported
|
* Support for multiple currencies and automatic update of exchange rates supported
|
||||||
* Powerful search and filter function, including parametric search (search for parts according to some specifications)
|
* Powerful search and filter function, including parametric search (search for parts according to some specifications)
|
||||||
* Easy migration from an existing PartKeepr instance (see [here]({%link partkeepr_migration.md %}))
|
* Easy migration from an existing PartKeepr instance (see [here]({%link partkeepr_migration.md %}))
|
||||||
* Use cloud providers (like Octopart, Digikey, Farnell or TME) to automatically get part information, datasheets and
|
* Use cloud providers (like Octopart, Digikey, farnell or TME) to automatically get part information, datasheets and prices for parts (see [here]({% link usage/information_provider_system.md %}))
|
||||||
prices for parts (see [here]({% link usage/information_provider_system.md %}))
|
|
||||||
* API to access Part-DB from other applications/scripts
|
|
||||||
* [Integration with KiCad]({%link usage/eda_integration.md %}): Use Part-DB as central datasource for your
|
|
||||||
KiCad and see available parts from Part-DB directly inside KiCad.
|
|
||||||
|
|
||||||
With these features Part-DB is useful to hobbyists, who want to keep track of their private electronic parts inventory,
|
With these features Part-DB is useful to hobbyists, who want to keep track of their private electronic parts inventory,
|
||||||
or makerspaces, where many users have should have (controlled) access to the shared inventory.
|
or makerspaces, where many users have should have (controlled) access to the shared inventory.
|
||||||
|
@ -58,31 +45,25 @@ or makerspaces, where many users have should have (controlled) access to the sha
|
||||||
Part-DB is also used by small companies and universities for managing their inventory.
|
Part-DB is also used by small companies and universities for managing their inventory.
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
Part-DB is licensed under the GNU Affero General Public License v3.0 (or at your opinion any later).
|
Part-DB is licensed under the GNU Affero General Public License v3.0 (or at your opinion any later).
|
||||||
This mostly means that you can use Part-DB for whatever you want (even use it commercially)
|
This mostly means that you can use Part-DB for whatever you want (even use it commercially)
|
||||||
as long as you publish the source code for every change you make under the AGPL, too.
|
as long as you publish the source code for every change you make under the AGPL, too.
|
||||||
|
|
||||||
See [LICENSE](https://github.com/Part-DB/Part-DB-symfony/blob/master/LICENSE) for more information.
|
See [LICENSE](https://github.com/Part-DB/Part-DB-symfony/blob/master/LICENSE) for more informations.
|
||||||
|
|
||||||
## Donate for development
|
## Donate for development
|
||||||
|
|
||||||
If you want to donate to the Part-DB developer, see the sponsor button in the top bar (next to the repo name).
|
If you want to donate to the Part-DB developer, see the sponsor button in the top bar (next to the repo name).
|
||||||
There you will find various methods to support development on a monthly or a one time base.
|
There you will find various methods to support development on a monthly or a one time base.
|
||||||
|
|
||||||
## Built with
|
## Built with
|
||||||
|
|
||||||
* [Symfony 5](https://symfony.com/): The main framework used for the serverside PHP
|
* [Symfony 5](https://symfony.com/): The main framework used for the serverside PHP
|
||||||
* [Bootstrap 5](https://getbootstrap.com/) and [Bootswatch](https://bootswatch.com/): Used as website theme
|
* [Bootstrap 5](https://getbootstrap.com/) and [Bootswatch](https://bootswatch.com/): Used as website theme
|
||||||
* [Fontawesome](https://fontawesome.com/): Used as icon set
|
* [Fontawesome](https://fontawesome.com/): Used as icon set
|
||||||
* [Hotwire Stimulus](https://stimulus.hotwired.dev/) and [Hotwire Turbo](https://turbo.hotwired.dev/): Frontend
|
* [Hotwire Stimulus](https://stimulus.hotwired.dev/) and [Hotwire Turbo](https://turbo.hotwired.dev/): Frontend Javascript
|
||||||
Javascript
|
|
||||||
|
|
||||||
## Authors
|
## Authors
|
||||||
|
* **Jan Böhmer** - *Inital work and Maintainer* - [Github](https://github.com/jbtronics/)
|
||||||
|
|
||||||
* **Jan Böhmer** - *Initial work and Maintainer* - [GitHub](https://github.com/jbtronics/)
|
See also the list of [contributors](https://github.com/Part-DB/Part-DB-symfony/graphs/contributors) who participated in this project.
|
||||||
|
|
||||||
See also the list of [contributors](https://github.com/Part-DB/Part-DB-symfony/graphs/contributors) who participated in
|
|
||||||
this project.
|
|
||||||
|
|
||||||
Based on the original Part-DB by Christoph Lechner and K. Jacobs
|
Based on the original Part-DB by Christoph Lechner and K. Jacobs
|
||||||
|
|
|
@ -7,176 +7,24 @@ nav_order: 1
|
||||||
|
|
||||||
# Choosing database: SQLite or MySQL
|
# Choosing database: SQLite or MySQL
|
||||||
|
|
||||||
Part-DB saves its data in a [relational (SQL) database](https://en.wikipedia.org/wiki/Relational_database).
|
Part-DB saves its data in a [relational (SQL) database](https://en.wikipedia.org/wiki/Relational_database). Part-DB supports either the use of [SQLite](https://www.sqlite.org/index.html) or [MySQL](https://www.mysql.com/) / [MariaDB](https://mariadb.org/) (which are mostly the same, except for some minor differences).
|
||||||
|
|
||||||
For this multiple database types are supported, currently these are:
|
|
||||||
|
|
||||||
* [SQLite](https://www.sqlite.org/index.html)
|
|
||||||
* [MySQL](https://www.mysql.com/) / [MariaDB](https://mariadb.org/) (which are mostly the same, except for some minor
|
|
||||||
differences)
|
|
||||||
* [PostgreSQL](https://www.postgresql.org/)
|
|
||||||
|
|
||||||
All these database types allow for the same basic functionality and allow Part-DB to run. However, there are some minor
|
|
||||||
differences between them, which might be important for you. Therefore the pros and cons of the different database types
|
|
||||||
are listed here.
|
|
||||||
|
|
||||||
{: .important }
|
{: .important }
|
||||||
You have to choose between the database types before you start using Part-DB and **you can not change it (easily) after
|
You have to choose between the database types before you start using Part-DB and **you can not change it (easily) after you have started creating data**. So you should choose the database type for your usecase (and possible future uses).
|
||||||
you have started creating data**. So you should choose the database type for your use case (and possible future uses).
|
|
||||||
|
|
||||||
## Comparison
|
## Comparison
|
||||||
|
|
||||||
### SQLite
|
**SQLite** is the default database type which is configured out of the box. All data is saved in a single file (normally `var/app.db` in the Part-DB folder) and no additional installation or configuration besides Part-DB is needed.
|
||||||
|
To use **MySQL/MariaDB** as database, you have to install and configure the MySQL server, configure it and create a database and user for Part-DB, which needs some additional work. When using docker you need an additional docker container, and volume for the data
|
||||||
|
|
||||||
#### Pros
|
When using **SQLite** The database can be backuped easily by just copying the SQLite file to a safe place. Ideally the **MySQL** database has to be dumped to a SQL file (using `mysqldump`). The `console partdb:backup` command can do this automatically
|
||||||
|
|
||||||
|
However SQLite does not support certain operations like regex search, which has to be emulated by PHP and therefore are pretty slow compared to the same operation at MySQL. In future there might be features that may only be available, when using MySQL.
|
||||||
|
|
||||||
* **Easy to use**: No additional installation or configuration is needed, just start Part-DB and it will work out of the box
|
In general MySQL might perform better for big Part-DB instances with many entries, lots of users and high activity, than SQLite.
|
||||||
* **Easy backup**: Just copy the SQLite file to a safe place, and you have a backup, which you can restore by copying it
|
|
||||||
back. No need to work with SQL dumps
|
|
||||||
|
|
||||||
#### Cons
|
## Conclusion and Suggestion
|
||||||
|
|
||||||
* **Performance**: SQLite is not as fast as MySQL or PostgreSQL, especially when using complex queries or many users.
|
When you are a hobbyist and use Part-DB for your own small inventory managment with only you as user (or maybe sometimes a few other people), then the easy to use SQLite database will be fine.
|
||||||
* **Emulated RegEx search**: SQLite does not support RegEx search natively. Part-DB can emulate it, however that is pretty slow.
|
|
||||||
* **Emualted natural sorting**: SQLite does not support natural sorting natively. Part-DB can emulate it, but it is pretty slow.
|
|
||||||
* **Limitations with Unicode**: SQLite has limitations in comparisons and sorting of Unicode characters, which might lead to
|
|
||||||
unexpected behavior when using non-ASCII characters in your data. For example `µ` (micro sign) is not seen as equal to
|
|
||||||
`μ` (greek minuscule mu), therefore searching for `µ` (micro sign) will not find parts containing `μ` (mu) and vice versa.
|
|
||||||
The other databases behave more intuitive in this case.
|
|
||||||
* **No advanced features**: SQLite do no support many of the advanced features of MySQL or PostgreSQL, which might be utilized
|
|
||||||
in future versions of Part-DB
|
|
||||||
|
|
||||||
|
When you are planning to have a very big database, with a lot of entries and many users which regulary (and concurrently) using Part-DB you should maybe use MySQL as this will scale better.
|
||||||
### MySQL/MariaDB
|
|
||||||
|
|
||||||
**If possible, it is recommended to use MariaDB 10.7+ (instead of MySQL), as it supports natural sorting of columns natively.**
|
|
||||||
|
|
||||||
#### Pros
|
|
||||||
|
|
||||||
* **Performance**: Compared to SQLite, MySQL/MariaDB will probably perform better, especially in large databases with many
|
|
||||||
users and high activity.
|
|
||||||
* **Natural Sorting**: MariaDB 10.7+ supports natural sorting of columns. On other databases it has to be emulated, which is pretty
|
|
||||||
slow.
|
|
||||||
* **Native RegEx search**: MySQL supports RegEx search natively, which is faster than emulating it in PHP.
|
|
||||||
* **Advanced features**: MySQL/MariaDB supports many advanced features, which might be utilized in future versions of Part-DB.
|
|
||||||
* **Full Unicode support**: MySQL/MariaDB has better support for Unicode characters, which makes it more intuitive to use
|
|
||||||
non-ASCII characters in your data.
|
|
||||||
|
|
||||||
#### Cons
|
|
||||||
|
|
||||||
* **Additional installation and configuration**: You have to install and configure the MySQL server, create a database and
|
|
||||||
user for Part-DB, which needs some additional work compared to SQLite.
|
|
||||||
* **Backup**: The MySQL database has to be dumped to a SQL file (using `mysqldump`). The `console partdb:backup` command can automate this.
|
|
||||||
|
|
||||||
|
|
||||||
### PostgreSQL
|
|
||||||
|
|
||||||
#### Pros
|
|
||||||
* **Performance**: PostgreSQL is known for its performance, especially in large databases with many users and high activity.
|
|
||||||
* **Advanced features**: PostgreSQL supports many advanced features, which might be utilized in future versions of Part-DB.
|
|
||||||
* **Full Unicode support**: PostgreSQL has better support for Unicode characters, which makes it more intuitive to use
|
|
||||||
non-ASCII characters in your data.
|
|
||||||
* **Native RegEx search**: PostgreSQL supports RegEx search natively, which is faster than emulating it in PHP.
|
|
||||||
* **Native Natural Sorting**: PostgreSQL supports natural sorting of columns natively in all versions and in general the support for it
|
|
||||||
is better than on MariaDB.
|
|
||||||
* **Support of transactional DDL**: PostgreSQL supports transactional DDL, which means that if you encounter a problem during a schema change,
|
|
||||||
the database will automatically rollback the changes. On MySQL/MariaDB you have to manually rollback the changes, by restoring from a database backup.
|
|
||||||
|
|
||||||
#### Cons
|
|
||||||
* **New backend**: The support of postgresql is new, and it was not tested as much as the other backends. There might be some bugs caused by this.
|
|
||||||
* **Additional installation and configuration**: You have to install and configure the PostgreSQL server, create a database and
|
|
||||||
user for Part-DB, which needs some additional work compared to SQLite.
|
|
||||||
* **Backup**: The PostgreSQL database has to be dumped to a SQL file (using `pg_dump`). The `console partdb:backup` command can automate this.
|
|
||||||
|
|
||||||
|
|
||||||
## Recommendation
|
|
||||||
|
|
||||||
When you are a hobbyist and use Part-DB for your own small inventory management with only you as user (or maybe sometimes
|
|
||||||
a few other people), then the easy-to-use SQLite database will be fine, as long as you can live with the limitations, stated above.
|
|
||||||
However using MariaDB (or PostgreSQL), has no disadvantages in that situation (besides the initial setup requirements), so you might
|
|
||||||
want to use it, to be prepared for future use cases.
|
|
||||||
|
|
||||||
When you are planning to have a very big database, with a lot of entries and many users which regularly using Part-DB, then you should
|
|
||||||
use MariaDB or PostgreSQL, as they will perform better in that situation and allow for more advanced features.
|
|
||||||
If you should use MariaDB or PostgreSQL depends on your personal preference and what you already have installed on your servers and
|
|
||||||
what you are familiar with.
|
|
||||||
|
|
||||||
## Using the different databases
|
|
||||||
|
|
||||||
The only difference in using the different databases, is a different value in the `DATABASE_URL` environment variable in the `.env.local` file
|
|
||||||
or in the `DATABASE_URL` environment variable in your server or container configuration. It has the shape of a URL, where the scheme (the part before `://`)
|
|
||||||
is the database type, and the rest is connection information.
|
|
||||||
|
|
||||||
**The env var format below is for the `env.local` file. It might work differently for other env configuration. E.g. in a docker-compose file you have to remove the quotes!**
|
|
||||||
|
|
||||||
### SQLite
|
|
||||||
|
|
||||||
```shell
|
|
||||||
DATABASE_URL="sqlite:///%kernel.project_dir%/var/app.db"
|
|
||||||
```
|
|
||||||
|
|
||||||
Here you just need to configure the path to the SQLite file, which is created by Part-DB when performing the database migrations.
|
|
||||||
The `%kernel.project_dir%` is a placeholder for the path to the project directory, which is replaced by the actual path by Symfony, so that you do not
|
|
||||||
need to specify the path manually. In the example the database will be created as `app.db` in the `var` directory of your Part-DB installation folder.
|
|
||||||
|
|
||||||
### MySQL/MariaDB
|
|
||||||
|
|
||||||
```shell
|
|
||||||
DATABASE_URL="mysql://user:password@127.0.0.1:3306/database?serverVersion=8.0.37"
|
|
||||||
```
|
|
||||||
|
|
||||||
Here you have to replace `user`, `password` and `database` with the credentials of the MySQL/MariaDB user and the database name you want to use.
|
|
||||||
The host (here 127.0.0.1) and port should also be specified according to your MySQL/MariaDB server configuration.
|
|
||||||
|
|
||||||
In the `serverVersion` parameter you can specify the version of the MySQL/MariaDB server you are using, in the way the server returns it
|
|
||||||
(e.g. `8.0.37` for MySQL and `10.4.14-MariaDB`). If you do not know it, you can leave the default value.
|
|
||||||
|
|
||||||
If you want to use a unix socket for the connection instead of a TCP connnection, you can specify the socket path in the `unix_socket` parameter.
|
|
||||||
```shell
|
|
||||||
DATABASE_URL="mysql://user:password@localhost/database?serverVersion=8.0.37&unix_socket=/var/run/mysqld/mysqld.sock"
|
|
||||||
```
|
|
||||||
|
|
||||||
### PostgreSQL
|
|
||||||
|
|
||||||
```shell
|
|
||||||
DATABASE_URL="postgresql://db_user:db_password@127.0.0.1:5432/db_name?serverVersion=12.19&charset=utf8"
|
|
||||||
```
|
|
||||||
|
|
||||||
Here you have to replace `db_user`, `db_password` and `db_name` with the credentials of the PostgreSQL user and the database name you want to use.
|
|
||||||
The host (here 127.0.0.1) and port should also be specified according to your PostgreSQL server configuration.
|
|
||||||
|
|
||||||
In the `serverVersion` parameter you can specify the version of the PostgreSQL server you are using, in the way the server returns it
|
|
||||||
(e.g. `12.19 (Debian 12.19-1.pgdg120+1)`). If you do not know it, you can leave the default value.
|
|
||||||
|
|
||||||
The `charset` parameter specify the character set of the database. It should be set to `utf8` to ensure that all characters are stored correctly.
|
|
||||||
|
|
||||||
If you want to use a unix socket for the connection instead of a TCP connnection, you can specify the socket path in the `host` parameter.
|
|
||||||
```shell
|
|
||||||
DATABASE_URL="postgresql://db_user@localhost/db_name?serverVersion=16.6&charset=utf8&host=/var/run/postgresql"
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
## Natural Sorting
|
|
||||||
|
|
||||||
Natural sorting is the sorting of strings in a way that numbers are sorted by their numerical value, not by their ASCII value.
|
|
||||||
|
|
||||||
For example in the classical binary sorting the string `DIP-4`, `DIP-8`, `DIP-16`, `DIP-28` would be sorted as following:
|
|
||||||
|
|
||||||
* `DIP-16`
|
|
||||||
* `DIP-28`
|
|
||||||
* `DIP-4`
|
|
||||||
* `DIP-8`
|
|
||||||
|
|
||||||
In natural sorting, it would be sorted as:
|
|
||||||
|
|
||||||
* `DIP-4`
|
|
||||||
* `DIP-8`
|
|
||||||
* `DIP-16`
|
|
||||||
* `DIP-28`
|
|
||||||
|
|
||||||
Part-DB can sort names in part tables and tree views naturally. PostgreSQL and MariaDB 10.7+ support natural sorting natively,
|
|
||||||
and it is automatically used if available.
|
|
||||||
|
|
||||||
For SQLite and MySQL < 10.7 it has to be emulated if wanted, which is pretty slow. Therefore it has to be explicity enabled by setting the
|
|
||||||
`DATABASE_EMULATE_NATURAL_SORT` environment variable to `1`. If it is 0 the classical binary sorting is used, on these databases. The emulations
|
|
||||||
might have some quirks and issues, so it is recommended to use a database which supports natural sorting natively, if you want to use it.
|
|
|
@ -7,34 +7,31 @@ nav_order: 12
|
||||||
|
|
||||||
# Email
|
# Email
|
||||||
|
|
||||||
Part-DB can communicate with its users via email.
|
Part-DB can communicate with its users via email.
|
||||||
At the moment this is only used to send password reset links, but in future this will be used for other things too.
|
At the moment this is only used to send password reset links, but in future this will be used for other things too.
|
||||||
|
|
||||||
To make emails work you have to properly configure a mail provider in Part-DB.
|
To make emails work you have to properly configure a mail provider in Part-DB.
|
||||||
|
|
||||||
## Configuration
|
## Configuration
|
||||||
|
|
||||||
Part-DB uses [Symfony Mailer](https://symfony.com/doc/current/mailer.html) to send emails, which supports multiple
|
Part-DB uses [Symfony Mailer](https://symfony.com/doc/current/mailer.html) to send emails, which supports multiple
|
||||||
automatic mail providers (like MailChimp or SendGrid). If you want to use one of these providers, check the Symfony
|
automatic mail providers (like MailChimp or SendGrid). If you want to use one of these providers, check the Symfony Mailer documentation for more information.
|
||||||
Mailer documentation for more information.
|
|
||||||
|
|
||||||
We will only cover the configuration of an SMTP provider here, which is sufficient for most use-cases.
|
We will only cover the configuration of a SMTP provider here, which is sufficient for most usecases.
|
||||||
You will need an email account, which you can use send emails from via password-bases SMTP authentication, this account
|
You will need an email account, which you can use send emails from via password-bases SMTP authentication, this account
|
||||||
should be dedicated to Part-DB.
|
should be dedicated to Part-DB.
|
||||||
|
|
||||||
To configure the SMTP provider, you have to set the following environment variables:
|
To configure the SMTP provider, you have to set the following environment variables:
|
||||||
|
|
||||||
`MAILER_DSN`: You have to provide the SMTP server address and the credentials for the email account here. The format is
|
`MAILER_DSN`: You have to provide the SMTP server address and the credentials for the email account here. The format is the following:
|
||||||
the following:
|
`smtp://<username>:<password>@<smtp-server-address>:<port>`. In most cases the username is the email address of the account, and the port is 587.
|
||||||
`smtp://<username>:<password>@<smtp-server-address>:<port>`. In most cases the username is the email address of the
|
|
||||||
account, and the port is 587.
|
|
||||||
So the resulting DSN could look like this: `smtp://j.doe@mail.invalid:SUPER_SECRET_PA$$WORD@smtp.mail.invalid:587`.
|
So the resulting DSN could look like this: `smtp://j.doe@mail.invalid:SUPER_SECRET_PA$$WORD@smtp.mail.invalid:587`.
|
||||||
|
|
||||||
`EMAIL_SENDER_EMAIL`: This is the email address which will be used as sender address for all emails sent by Part-DB.
|
`EMAIL_SENDER_EMAIL`: This is the email address which will be used as sender address for all emails sent by Part-DB.
|
||||||
This should be the same email address as the one used in the `MAILER_DSN` (the email address of your email account):
|
This should be the same email address as the one used in the `MAILER_DSN` (the email adress of your email account):
|
||||||
e.g. `j.doe@mail.invalid`.
|
e.g. `j.doe@mail.invalid`.
|
||||||
|
|
||||||
`EMAIL_SENDER_NAME`: This is the name which will be used as sender name for all emails sent by Part-DB.
|
`EMAIL_SENDER_NAME`: This is the name which will be used as sender name for all emails sent by Part-DB.
|
||||||
This can be anything you want, e.g. `My Part-DB Mailer`.
|
This can be anything you want, e.g. `My Part-DB Mailer`.
|
||||||
|
|
||||||
|
|
||||||
Now you can enable the possibility to reset password by setting the `ALLOW_EMAIL_PW_RESET` env to `1` (or `true`).
|
Now you can enable the possibility to reset password by setting the `ALLOW_EMAIL_PW_RESET` env to `1` (or `true`).
|
|
@ -6,6 +6,4 @@ has_children: true
|
||||||
---
|
---
|
||||||
|
|
||||||
# Installation
|
# Installation
|
||||||
Below you can find some guides to install Part-DB.
|
Below you can find some guides to install Part-DB.
|
||||||
|
|
||||||
For the hobbyists without much experience, we recommend the docker installation or direct installation on debian.
|
|
|
@ -7,23 +7,19 @@ nav_order: 2
|
||||||
|
|
||||||
# Installation of Part-DB via docker
|
# Installation of Part-DB via docker
|
||||||
|
|
||||||
Part-DB can be installed containerized via docker. This is the easiest way to get Part-DB up and running and works on
|
Part-DB can be installed containerized via docker. This is the easiest way to get Part-DB up and running and works on all platforms,
|
||||||
all platforms,
|
where docker is available (especially recommended for Windows and MacOS).
|
||||||
where docker is available (especially recommended for Windows and macOS).
|
|
||||||
|
|
||||||
{: .warning }
|
{: .warning }
|
||||||
> The methods described here, configure PHP without HTTPS and therefore should only be used locally in a trusted
|
> The methods described here, configure PHP without HTTPS and therefore should only be used locally in a trusted network.
|
||||||
> network.
|
|
||||||
> If you want to expose Part-DB to the internet, you have to configure a reverse proxy with an SSL certificate!
|
> If you want to expose Part-DB to the internet, you have to configure a reverse proxy with an SSL certificate!
|
||||||
|
|
||||||
It is recommended to install Part-DB on a 64-bit system, as the 32-bit version of PHP is affected by the
|
|
||||||
[Year 2038 problem](https://en.wikipedia.org/wiki/Year_2038_problem) and can not handle dates after 2038 correctly.
|
|
||||||
|
|
||||||
## Docker-compose
|
## Docker-compose
|
||||||
|
|
||||||
Docker-compose configures the needed images and automatically creates the needed containers and volumes.
|
Docker-compose configures the needed images and automatically creates the needed containers and volumes.
|
||||||
|
|
||||||
1. Install docker and docker-compose as described under https://docs.docker.com/compose/install/
|
|
||||||
|
1. Install docker and docker-compose like described under https://docs.docker.com/compose/install/
|
||||||
2. Create a folder where the Part-DB data should live
|
2. Create a folder where the Part-DB data should live
|
||||||
3. Create a file named docker-compose.yaml with the following content:
|
3. Create a file named docker-compose.yaml with the following content:
|
||||||
|
|
||||||
|
@ -47,18 +43,11 @@ services:
|
||||||
- DATABASE_URL=sqlite:///%kernel.project_dir%/var/db/app.db
|
- DATABASE_URL=sqlite:///%kernel.project_dir%/var/db/app.db
|
||||||
# In docker env logs will be redirected to stderr
|
# In docker env logs will be redirected to stderr
|
||||||
- APP_ENV=docker
|
- APP_ENV=docker
|
||||||
|
|
||||||
# Uncomment this, if you want to use the automatic database migration feature. With this you have you do not have to
|
|
||||||
# run the doctrine:migrations:migrate commands on installation or upgrade. A database backup is written to the uploads/
|
|
||||||
# folder (under .automigration-backup), so you can restore it, if the migration fails.
|
|
||||||
# This feature is currently experimental, so use it at your own risk!
|
|
||||||
# - DB_AUTOMIGRATE=true
|
|
||||||
|
|
||||||
# You can configure Part-DB using environment variables
|
# You can configure Part-DB using environment variables
|
||||||
# Below you can find the most essential ones predefined
|
# Below you can find the most essential ones predefined
|
||||||
# However you can add any other environment configuration you want here
|
# However you can add add any other environment configuration you want here
|
||||||
# See .env file for all available options or https://docs.part-db.de/configuration.html
|
# See .env file for all available options or https://docs.part-db.de/configuration.html
|
||||||
# !!! Do not use quotes around the values, as they will be interpreted as part of the value and this will lead to errors !!!
|
|
||||||
|
|
||||||
# The language to use serverwide as default (en, de, ru, etc.)
|
# The language to use serverwide as default (en, de, ru, etc.)
|
||||||
- DEFAULT_LANG=en
|
- DEFAULT_LANG=en
|
||||||
|
@ -75,35 +64,23 @@ services:
|
||||||
# Use gravatars for user avatars, when user has no own avatar defined
|
# Use gravatars for user avatars, when user has no own avatar defined
|
||||||
- USE_GRAVATAR=0
|
- USE_GRAVATAR=0
|
||||||
|
|
||||||
# Override value if you want to show a given text on homepage.
|
# Override value if you want to show to show a given text on homepage.
|
||||||
# When this is empty the content of config/banner.md is used as banner
|
# When this is empty the content of config/banner.md is used as banner
|
||||||
#- BANNER=This is a test banner<br>with a line break
|
#- BANNER=This is a test banner<br>with a line break
|
||||||
|
|
||||||
# If you use a reverse proxy in front of Part-DB, you must configure the trusted proxies IP addresses here (see reverse proxy documentation for more information):
|
|
||||||
# - TRUSTED_PROXIES=127.0.0.0/8,::1,10.0.0.0/8,172.16.0.0/12,192.168.0.0/16
|
|
||||||
```
|
```
|
||||||
|
4. Customize the settings by changing the environment variables (or add new ones). See [Configuration]({% link configuration.md %}) for more information.
|
||||||
4. Customize the settings by changing the environment variables (or adding new ones). See [Configuration]({% link
|
|
||||||
configuration.md %}) for more information.
|
|
||||||
5. Inside the folder, run
|
5. Inside the folder, run
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
docker-compose up -d
|
docker-compose up -d
|
||||||
```
|
```
|
||||||
|
6. Create the inital database with
|
||||||
6. Create the initial database with
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
docker exec --user=www-data partdb php bin/console doctrine:migrations:migrate
|
docker exec --user=www-data partdb php bin/console doctrine:migrations:migrate
|
||||||
```
|
```
|
||||||
|
|
||||||
and watch for the password output
|
and watch for the password output
|
||||||
|
6. Part-DB is available under `http://localhost:8080` and you can log in with username `admin` and the password shown before
|
||||||
|
|
||||||
6. Part-DB is available under `http://localhost:8080` and you can log in with the username `admin` and the password shown
|
The docker image uses a SQLite database and all data (database, uploads and other media) is put into folders relative to the docker-compose.yml.
|
||||||
before
|
|
||||||
|
|
||||||
The docker image uses a SQLite database and all data (database, uploads, and other media) is put into folders relative to
|
|
||||||
the docker-compose.yml.
|
|
||||||
|
|
||||||
### MySQL
|
### MySQL
|
||||||
|
|
||||||
|
@ -136,12 +113,6 @@ services:
|
||||||
# In docker env logs will be redirected to stderr
|
# In docker env logs will be redirected to stderr
|
||||||
- APP_ENV=docker
|
- APP_ENV=docker
|
||||||
|
|
||||||
# Uncomment this, if you want to use the automatic database migration feature. With this you have you do not have to
|
|
||||||
# run the doctrine:migrations:migrate commands on installation or upgrade. A database backup is written to the uploads/
|
|
||||||
# folder (under .automigration-backup), so you can restore it, if the migration fails.
|
|
||||||
# This feature is currently experimental, so use it at your own risk!
|
|
||||||
# - DB_AUTOMIGRATE=true
|
|
||||||
|
|
||||||
# You can configure Part-DB using environment variables
|
# You can configure Part-DB using environment variables
|
||||||
# Below you can find the most essential ones predefined
|
# Below you can find the most essential ones predefined
|
||||||
# However you can add add any other environment configuration you want here
|
# However you can add add any other environment configuration you want here
|
||||||
|
@ -169,8 +140,7 @@ services:
|
||||||
database:
|
database:
|
||||||
container_name: partdb_database
|
container_name: partdb_database
|
||||||
image: mysql:8.0
|
image: mysql:8.0
|
||||||
restart: unless-stopped
|
command: --default-authentication-plugin=mysql_native_password
|
||||||
command: --default-authentication-plugin=mysql_native_password --log-bin-trust-function-creators=1
|
|
||||||
environment:
|
environment:
|
||||||
# Change this Password
|
# Change this Password
|
||||||
MYSQL_ROOT_PASSWORD: SECRET_ROOT_PASSWORD
|
MYSQL_ROOT_PASSWORD: SECRET_ROOT_PASSWORD
|
||||||
|
@ -186,10 +156,8 @@ services:
|
||||||
```
|
```
|
||||||
|
|
||||||
### Update Part-DB
|
### Update Part-DB
|
||||||
|
|
||||||
You can update Part-DB by pulling the latest image and restarting the container.
|
You can update Part-DB by pulling the latest image and restarting the container.
|
||||||
Then you have to run the database migrations again
|
Then you have to run the database migrations again
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
docker-compose pull
|
docker-compose pull
|
||||||
docker-compose up -d
|
docker-compose up -d
|
||||||
|
@ -197,30 +165,19 @@ docker exec --user=www-data partdb php bin/console doctrine:migrations:migrate
|
||||||
```
|
```
|
||||||
|
|
||||||
## Direct use of docker image
|
## Direct use of docker image
|
||||||
|
You can use the `jbtronics/part-db1:master` image directly. You have to expose the port 80 to a host port and configure volumes for `/var/www/html/uploads` and `/var/www/html/public/media`.
|
||||||
|
|
||||||
You can use the `jbtronics/part-db1:master` image directly. You have to expose port 80 to a host port and configure
|
If you want to use SQLite database (which is default), you have to configure Part-DB to put the database file in a mapped volume via the `DATABASE_URL` environment variable.
|
||||||
volumes for `/var/www/html/uploads` and `/var/www/html/public/media`.
|
For example if you set `DATABASE_URL=sqlite:///%kernel.project_dir%/var/db/app.db` then you will have to map the `/var/www/html/var/db/` folder to the docker container (see docker-compose.yaml for example).
|
||||||
|
|
||||||
If you want to use SQLite database (which is default), you have to configure Part-DB to put the database file in a
|
You also have to create the database like described above in step 4.
|
||||||
mapped volume via the `DATABASE_URL` environment variable.
|
|
||||||
For example, if you set `DATABASE_URL=sqlite:///%kernel.project_dir%/var/db/app.db` then you will have to map
|
|
||||||
the `/var/www/html/var/db/` folder to the docker container (see docker-compose.yaml for example).
|
|
||||||
|
|
||||||
You also have to create the database as described above in step 4.
|
|
||||||
|
|
||||||
## Running console commands
|
## Running console commands
|
||||||
|
You can run the console commands described in README by executing `docker exec --user=www-data -it partdb bin/console [command]`
|
||||||
You can run the console commands described in README by
|
|
||||||
executing `docker exec --user=www-data -it partdb bin/console [command]`
|
|
||||||
|
|
||||||
{: .warning }
|
|
||||||
> If you run a root console inside the container, and wanna execute commands on the webserver behalf, be sure to use `sudo -E` command (with the `-E` flag) to preserve env variables from the current shell.
|
|
||||||
> Otherwise Part-DB console might use the wrong configuration to execute commands.
|
|
||||||
|
|
||||||
## Troubleshooting
|
## Troubleshooting
|
||||||
|
|
||||||
*Login is not possible. Login page is just reloading and no error message is shown or something like "CSFR token invalid"*:
|
*Login not possible. Login page is just reloading and no error message is shown or something like "CSFR token invalid"*:
|
||||||
|
|
||||||
Clear all cookies in your browser or use an incognito tab for Part-DB.
|
Clear all cookies in your browser or use a inkognito tab for Part-DB.
|
||||||
This is related to the fact that Part-DB can not set cookies via HTTP after some webpages have set cookies before under
|
This related to the fact that Part-DB can not set cookies via HTTP, after some webpage has set cookies before under localhost via https. This is a security mechanism of the browser and can not be bypassed by Part-DB.
|
||||||
localhost via HTTPS. This is a security mechanism of the browser and can not be bypassed by Part-DB.
|
|
||||||
|
|
|
@ -6,41 +6,25 @@ nav_order: 4
|
||||||
---
|
---
|
||||||
|
|
||||||
# Part-DB installation guide for Debian 11 (Bullseye)
|
# Part-DB installation guide for Debian 11 (Bullseye)
|
||||||
|
This guide shows you how to install Part-DB directly on Debian 11 using apache2 and SQLite. This guide should work with recent Ubuntu and other Debian based distributions with little to no changes.
|
||||||
This guide shows you how to install Part-DB directly on Debian 11 using apache2 and SQLite. This guide should work with
|
Depending on what you want to do, using the prebuilt docker images may be a better choice, as you dont need to install this much dependencies. See **TODO** for more information of the docker installation.
|
||||||
recent Ubuntu and other Debian-based distributions with little to no changes.
|
|
||||||
Depending on what you want to do, using the prebuilt docker images may be a better choice, as you don't need to install
|
|
||||||
this many dependencies. See [here]({% link installation/installation_docker.md %}) for more information on the docker
|
|
||||||
installation.
|
|
||||||
|
|
||||||
{: .warning }
|
{: .warning }
|
||||||
> The methods described here, configure PHP without HTTPS and therefore should only be used locally in a trusted
|
> The methods described here, configure PHP without HTTPS and therefore should only be used locally in a trusted network.
|
||||||
> network.
|
|
||||||
> If you want to expose Part-DB to the internet, you HAVE to configure an SSL connection!
|
> If you want to expose Part-DB to the internet, you HAVE to configure an SSL connection!
|
||||||
|
|
||||||
It is recommended to install Part-DB on a 64-bit system, as the 32-bit version of PHP is affected by the
|
|
||||||
[Year 2038 problem](https://en.wikipedia.org/wiki/Year_2038_problem) and can not handle dates after 2038 correctly.
|
|
||||||
|
|
||||||
## Installation with SQLite database
|
## Installation with SQLite database
|
||||||
|
|
||||||
### Install prerequisites
|
### Install prerequisites
|
||||||
|
|
||||||
For the installation of Part-DB, we need some prerequisites. They can be installed by running the following command:
|
For the installation of Part-DB, we need some prerequisites. They can be installed by running the following command:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
sudo apt install git curl zip ca-certificates software-properties-common apt-transport-https lsb-release nano wget
|
sudo apt install git curl zip ca-certificates software-properties-common apt-transport-https lsb-release nano wget
|
||||||
```
|
```
|
||||||
|
|
||||||
### Install PHP and apache2
|
### Install PHP and apache2
|
||||||
|
Part-DB is written in [PHP](https://php.net) and therefore needs an PHP interpreter to run. Part-DB needs PHP 8.1 or higher, however it is recommended to use the most recent version of PHP for performance reasons and future compatibility.
|
||||||
|
|
||||||
Part-DB is written in [PHP](https://php.net) and therefore needs a PHP interpreter to run. Part-DB needs PHP 8.1 or
|
As Debian 11 does not ship PHP 8.1 in it's default repositories, we have to add a repository for it. You can skip this step if your distribution is shipping a recent version of PHP or you want to use the built-in PHP version. If you are using Debian 12, you can skip this step, as PHP 8.1 is already included in the default repositories.
|
||||||
higher. However, it is recommended to use the most recent version of PHP for performance reasons and future
|
|
||||||
compatibility.
|
|
||||||
|
|
||||||
As Debian 11 does not ship PHP 8.1 in its default repositories, we have to add a repository for it. You can skip this
|
|
||||||
step if your distribution is shipping a recent version of PHP or you want to use the built-in PHP version. If you are
|
|
||||||
using Debian 12, you can skip this step, as PHP 8.1 is already included in the default repositories.
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Add sury repository for PHP 8.1
|
# Add sury repository for PHP 8.1
|
||||||
sudo curl -sSL https://packages.sury.org/php/README.txt | sudo bash -x
|
sudo curl -sSL https://packages.sury.org/php/README.txt | sudo bash -x
|
||||||
|
@ -48,21 +32,14 @@ sudo curl -sSL https://packages.sury.org/php/README.txt | sudo bash -x
|
||||||
# Update package list
|
# Update package list
|
||||||
sudo apt update && sudo apt upgrade
|
sudo apt update && sudo apt upgrade
|
||||||
```
|
```
|
||||||
|
Now you can install PHP 8.1 and required packages (change the 8.1 in the package version according to the version you want to use):
|
||||||
Now you can install PHP 8.1 and the required packages (change the 8.1 in the package version according to the version you
|
|
||||||
want to use):
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
sudo apt install php8.1 libapache2-mod-php8.1 php8.1-opcache php8.1-curl php8.1-gd php8.1-mbstring php8.1-xml php8.1-bcmath php8.1-intl php8.1-zip php8.1-xsl php8.1-sqlite3 php8.1-mysql
|
sudo apt install php8.1 libapache2-mod-php8.1 php8.1-opcache php8.1-curl php8.1-gd php8.1-mbstring php8.1-xml php8.1-bcmath php8.1-intl php8.1-zip php8.1-xsl php8.1-sqlite3 php8.1-mysql
|
||||||
```
|
```
|
||||||
|
|
||||||
The apache2 webserver should be already installed with this command and configured basically.
|
The apache2 webserver should be already installed with this command and configured basically.
|
||||||
|
|
||||||
### Install composer
|
### Install composer
|
||||||
|
Part-DB uses [composer](https://getcomposer.org/) to install required PHP libraries. As the versions shipped in the repositories is pretty old we install it manually:
|
||||||
Part-DB uses [composer](https://getcomposer.org/) to install required PHP libraries. As the version shipped in the
|
|
||||||
repositories is pretty old, we will install it manually:
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Download composer installer script
|
# Download composer installer script
|
||||||
wget -O /tmp/composer-setup.php https://getcomposer.org/installer
|
wget -O /tmp/composer-setup.php https://getcomposer.org/installer
|
||||||
|
@ -73,10 +50,7 @@ chmod +x /usr/local/bin/composer
|
||||||
```
|
```
|
||||||
|
|
||||||
### Install yarn and nodejs
|
### Install yarn and nodejs
|
||||||
|
To build the frontend (the user interface) Part-DB uses [yarn](https://yarnpkg.com/). As it dependens on nodejs and the shipped versions are pretty old, we install new versions from offical nodejs repository:
|
||||||
To build the front end (the user interface) Part-DB uses [yarn](https://yarnpkg.com/). As it depends on Node.js and the
|
|
||||||
shipped versions are pretty old, we install new versions from the official Node.js repository:
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Add recent node repository (nodejs 18 is supported until 2025)
|
# Add recent node repository (nodejs 18 is supported until 2025)
|
||||||
curl -sL https://deb.nodesource.com/setup_18.x | sudo -E bash -
|
curl -sL https://deb.nodesource.com/setup_18.x | sudo -E bash -
|
||||||
|
@ -85,7 +59,6 @@ sudo apt install nodejs
|
||||||
```
|
```
|
||||||
|
|
||||||
We can install yarn with the following commands:
|
We can install yarn with the following commands:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Add yarn repository
|
# Add yarn repository
|
||||||
curl -sL https://dl.yarnpkg.com/debian/pubkey.gpg | gpg --dearmor | sudo tee /usr/share/keyrings/yarnkey.gpg >/dev/null
|
curl -sL https://dl.yarnpkg.com/debian/pubkey.gpg | gpg --dearmor | sudo tee /usr/share/keyrings/yarnkey.gpg >/dev/null
|
||||||
|
@ -95,64 +68,46 @@ sudo apt update && sudo apt install yarn
|
||||||
```
|
```
|
||||||
|
|
||||||
### Create a folder for Part-DB and download it
|
### Create a folder for Part-DB and download it
|
||||||
|
We now have all prerequisites installed and can start to install Part-DB. We will create a folder for Part-DB in a webfolder of apache2 and download it to this folder. The downloading is done via git, which allows you to update easily later.
|
||||||
We now have all prerequisites installed and can start to install Part-DB. We will create a folder for Part-DB in the
|
|
||||||
webroot of apache2 and download it to this folder. The downloading is done via git, which allows you to update easily
|
|
||||||
later.
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Download Part-DB into the new folder /var/www/partdb
|
# Download Part-DB into the new folder /var/www/partdb
|
||||||
git clone https://github.com/Part-DB/Part-DB-symfony.git /var/www/partdb
|
git clone https://github.com/Part-DB/Part-DB-symfony.git /var/www/partdb
|
||||||
```
|
```
|
||||||
|
|
||||||
By default, you are now on the latest development version. In most cases, you want to use the latest stable version. You
|
By default you are now on the latest development version. In most cases you want to use the latest stable version. You can switch to the latest stable version (tagged) by running the following command:
|
||||||
can switch to the latest stable version (tagged) by running the following command:
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# This finds the latest release/tag and checks it out
|
# This finds the latest release/tag and checks it out
|
||||||
git checkout $(git describe --tags $(git rev-list --tags --max-count=1))
|
git checkout $(git describe --tags $(git rev-list --tags --max-count=1))
|
||||||
```
|
```
|
||||||
|
Alternatively you can checkout a specific version by running (see [GitHub Relases page](https://github.com/Part-DB/Part-DB-server/releases) for a list of available versions):
|
||||||
Alternatively, you can check out a specific version by running (
|
|
||||||
see [GitHub Releases page](https://github.com/Part-DB/Part-DB-server/releases) for a list of available versions):
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# This checks out the version 1.5.2
|
# This checks out the version 1.5.2
|
||||||
git checkout v1.5.2
|
git checkout v1.5.2
|
||||||
```
|
```
|
||||||
|
|
||||||
Change ownership of the files to the apache user:
|
Change ownership of the files to the apache user:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
chown -R www-data:www-data /var/www/partdb
|
chown -R www-data:www-data /var/www/partdb
|
||||||
```
|
```
|
||||||
|
|
||||||
For the next steps we should be in the Part-DB folder, so move into it:
|
For the next steps we should be in the Part-DB folder, so move into it:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cd /var/www/partdb
|
cd /var/www/partdb
|
||||||
```
|
```
|
||||||
|
|
||||||
### Create configuration for Part-DB
|
### Create configuration for Part-DB
|
||||||
|
The basic configuration of Part-DB is done by a `.env.local` file in the main directory. Create on by from the default configuration:
|
||||||
The basic configuration of Part-DB is done by a `.env.local` file in the main directory. Create on by from the default
|
|
||||||
configuration:
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cp .env .env.local
|
cp .env .env.local
|
||||||
```
|
```
|
||||||
|
|
||||||
In your `.env.local` you can configure Part-DB according to your wishes. A full list of configuration options can be
|
In your `.env.local` you can configure Part-DB according to your wishes. A full list of configuration options can be found [here](../configuration.md).
|
||||||
found [here](../configuration.md).
|
|
||||||
Other configuration options like the default language or default currency can be found in `config/parameters.yaml`.
|
Other configuration options like the default language or default currency can be found in `config/parameters.yaml`.
|
||||||
|
|
||||||
Please check that the `partdb.default_currency` value in `config/parameters.yaml` matches your mainly used currency, as
|
Please check that the `partdb.default_currency` value in `config/parameters.yaml` matches your mainly used currency, as this can not be changed after creating price informations.
|
||||||
this can not be changed after creating price information.
|
|
||||||
|
|
||||||
### Install dependencies for Part-DB and build frontend
|
### Install dependencies for Part-DB and build frontend
|
||||||
|
|
||||||
Part-DB depends on several other libraries and components. Install them by running the following commands:
|
Part-DB depends on several other libraries and components. Install them by running the following commands:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Install composer dependencies (please note the sudo command, to run it under the web server user)
|
# Install composer dependencies (please note the sudo command, to run it under the web server user)
|
||||||
sudo -u www-data composer install --no-dev -o
|
sudo -u www-data composer install --no-dev -o
|
||||||
|
@ -166,47 +121,32 @@ sudo yarn build
|
||||||
### Clear cache
|
### Clear cache
|
||||||
|
|
||||||
To ensure everything is working, clear the cache:
|
To ensure everything is working, clear the cache:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
sudo -u www-data php bin/console cache:clear
|
sudo -u www-data php bin/console cache:clear
|
||||||
```
|
```
|
||||||
|
|
||||||
### Check if everything is installed
|
### Check if everything is installed
|
||||||
|
|
||||||
To check if everything is installed, run the following command:
|
To check if everything is installed, run the following command:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
sudo -u www-data php bin/console partdb:check-requirements
|
sudo -u www-data php bin/console partdb:check-requirements
|
||||||
```
|
```
|
||||||
|
The most things should be green, and no red ones. Yellow messages means optional dependencies which are not important but can improve performance and functionality.
|
||||||
Most things should be green, and no red ones. Yellow messages mean optional dependencies which are not important
|
|
||||||
but can improve performance and functionality.
|
|
||||||
|
|
||||||
### Create a database for Part-DB
|
### Create a database for Part-DB
|
||||||
|
Part-DB by default uses a file based sqlite database to store the data. Use the following command to create the database. The database will normally created at `/var/www/partdb/var/app.db`.
|
||||||
Part-DB by default uses a file-based SQLite database to store the data. Use the following command to create the
|
|
||||||
database. The database will normally be created at `/var/www/partdb/var/app.db`.
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
sudo -u www-data php bin/console doctrine:migrations:migrate
|
sudo -u www-data php bin/console doctrine:migrations:migrate
|
||||||
```
|
```
|
||||||
|
|
||||||
The command will warn you about schema changes and potential data loss. Continue with typing `yes`.
|
The command will warn you about schema changes and potential data loss. Continue with typing `yes`.
|
||||||
|
|
||||||
The command will output several lines of information. Somewhere should be a yellow background message
|
The command will output several lines of informations. Somewhere should be a a yellow background message like `The initial password for the "admin" user is: f502481134`. Write down this password as you will need it later for inital login.
|
||||||
like `The initial password for the "admin" user is: f502481134`. Write down this password as you will need it later for the initial login.
|
|
||||||
|
|
||||||
### Configure apache2 to show Part-DB
|
### Configure apache2 to show Part-DB
|
||||||
|
Part-DB is now configured, but we have to say apache2 to serve Part-DB as web application. This is done by creating a new apache site:
|
||||||
Part-DB is now configured, but we have to say apache2 to serve Part-DB as web application. This is done by creating a
|
|
||||||
new apache site:
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
sudo nano /etc/apache2/sites-available/partdb.conf
|
sudo nano /etc/apache2/sites-available/partdb.conf
|
||||||
```
|
```
|
||||||
|
|
||||||
and add the following content (change ServerName and ServerAlias to your needs):
|
and add the following content (change ServerName and ServerAlias to your needs):
|
||||||
|
|
||||||
```
|
```
|
||||||
<VirtualHost *:80>
|
<VirtualHost *:80>
|
||||||
ServerName partdb.lan
|
ServerName partdb.lan
|
||||||
|
@ -223,44 +163,33 @@ and add the following content (change ServerName and ServerAlias to your needs):
|
||||||
CustomLog /var/log/apache2/partdb_access.log combined
|
CustomLog /var/log/apache2/partdb_access.log combined
|
||||||
</VirtualHost>
|
</VirtualHost>
|
||||||
```
|
```
|
||||||
|
|
||||||
Activate the new site by:
|
Activate the new site by:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
sudo ln -s /etc/apache2/sites-available/partdb.conf /etc/apache2/sites-enabled/partdb.conf
|
sudo ln -s /etc/apache2/sites-available/partdb.conf /etc/apache2/sites-enabled/partdb.conf
|
||||||
```
|
```
|
||||||
|
|
||||||
Configure apache to show pretty URL paths for Part-DB (`/label/dialog` instead of `/index.php/label/dialog`):
|
Configure apache to show pretty URL pathes for Part-DB (`/label/dialog` instead of `/index.php/label/dialog`):
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
sudo a2enmod rewrite
|
sudo a2enmod rewrite
|
||||||
```
|
```
|
||||||
|
|
||||||
If you want to access Part-DB via the IP-Address of the server, instead of the domain name, you have to remove the
|
If you want to access Part-DB via the IP-Address of the server, instead of the domain name, you have to remove the apache2 default configuration with:
|
||||||
apache2 default configuration with:
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
sudo rm /etc/apache2/sites-enabled/000-default.conf
|
sudo rm /etc/apache2/sites-enabled/000-default.conf
|
||||||
```
|
```
|
||||||
|
|
||||||
Restart the apache2 webserver with:
|
Restart the apache2 webserver with:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
sudo service apache2 restart
|
sudo service apache2 restart
|
||||||
```
|
```
|
||||||
|
|
||||||
and Part-DB should now be available under `http://YourServerIP` (or `http://partdb.lan` if you configured DNS in your
|
and Part-DB should now be available under `http://YourServerIP` (or `http://partdb.lan` if you configured DNS in your network to point on the server).
|
||||||
network to point to the server).
|
|
||||||
|
|
||||||
### Login to Part-DB
|
### Login to Part-DB
|
||||||
|
Navigate to the Part-DB web interface and login via the user icon in the top right corner. You can login using the username `admin` and the password you have written down earlier.
|
||||||
Navigate to the Part-DB web interface and login via the user icon in the top right corner. You can log in using the
|
|
||||||
username `admin` and the password you have written down earlier.
|
|
||||||
|
|
||||||
## Update Part-DB
|
## Update Part-DB
|
||||||
|
|
||||||
If you want to update your existing Part-DB installation, you just have to run the following commands:
|
If you want to update your existing Part-DB installation, you just have to run the following commands:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Move into Part-DB folder
|
# Move into Part-DB folder
|
||||||
cd /var/www/partdb
|
cd /var/www/partdb
|
||||||
|
@ -289,8 +218,7 @@ sudo -u www-data php bin/console cache:clear
|
||||||
```
|
```
|
||||||
|
|
||||||
## MySQL/MariaDB database
|
## MySQL/MariaDB database
|
||||||
|
To use a MySQL database, follow the steps from above (except the creation of database, we will do this later).
|
||||||
To use a MySQL database, follow the steps from above (except the creation of the database, we will do this later).
|
|
||||||
Debian 11 does not ship MySQL in its repositories anymore, so we use the compatible MariaDB instead:
|
Debian 11 does not ship MySQL in its repositories anymore, so we use the compatible MariaDB instead:
|
||||||
|
|
||||||
1. Install maria-db with:
|
1. Install maria-db with:
|
||||||
|
@ -300,11 +228,9 @@ sudo apt update && sudo apt install mariadb-server
|
||||||
```
|
```
|
||||||
|
|
||||||
2. Configure maria-db with:
|
2. Configure maria-db with:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
sudo mysql_secure_installation
|
sudo mysql_secure_installation
|
||||||
```
|
```
|
||||||
|
|
||||||
When asked for the root password, just press enter, as we have not set a root password yet.
|
When asked for the root password, just press enter, as we have not set a root password yet.
|
||||||
In the next steps you are asked if you want to switch to unix_socket authentication, answer with `n` and press enter.
|
In the next steps you are asked if you want to switch to unix_socket authentication, answer with `n` and press enter.
|
||||||
Then you are asked if you want to remove anonymous users, answer with `y` and press enter.
|
Then you are asked if you want to remove anonymous users, answer with `y` and press enter.
|
||||||
|
@ -313,42 +239,33 @@ Then you are asked if you want to remove the test database and access to it, ans
|
||||||
Then you are asked if you want to reload the privilege tables now, answer with `y` and press enter.
|
Then you are asked if you want to reload the privilege tables now, answer with `y` and press enter.
|
||||||
|
|
||||||
3. Create a new database and user for Part-DB: Run the following commands:
|
3. Create a new database and user for Part-DB: Run the following commands:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
sudo mariadb
|
sudo mariadb
|
||||||
```
|
```
|
||||||
|
|
||||||
A SQL shell will open, in which you can run the following commands to create a new database and user for Part-DB.
|
A SQL shell will open, in which you can run the following commands to create a new database and user for Part-DB.
|
||||||
Replace 'YOUR_SECRET_PASSWORD' with a secure password.
|
Replace 'YOUR_SECRET_PASSWORD' with a secure password.
|
||||||
|
|
||||||
```sql
|
```sql
|
||||||
CREATE DATABASE partdb;
|
CREATE DATABASE partdb;
|
||||||
GRANT ALL PRIVILEGES ON partdb.* TO 'partdb'@'localhost' IDENTIFIED BY 'YOUR_SECRET_PASSWORD';
|
GRANT ALL PRIVILEGES ON partdb.* TO 'partdb'@'localhost' IDENTIFIED BY 'YOUR_SECRET_PASSWORD';
|
||||||
```
|
```
|
||||||
|
Finally save the changes with:
|
||||||
Finally, save the changes with:
|
|
||||||
|
|
||||||
```sql
|
```sql
|
||||||
FLUSH PRIVILEGES;
|
FLUSH PRIVILEGES;
|
||||||
```
|
```
|
||||||
|
|
||||||
and exit the SQL shell with:
|
and exit the SQL shell with:
|
||||||
|
|
||||||
```sql
|
```sql
|
||||||
exit
|
exit
|
||||||
```
|
```
|
||||||
|
|
||||||
4. Configure Part-DB to use the new database. Open your `.env.local` file and search the line `DATABASE_URL`.
|
4. Configure Part-DB to use the new database. Open your `.env.local` file and search the line `DATABASE_URL`.
|
||||||
Change it to the following (you have to replace `YOUR_SECRET_PASSWORD` with the password you have chosen in step 3):
|
Change it to the following (you have to replace `YOUR_SECRET_PASSWORD` with the password you have choosen in step 3):
|
||||||
|
|
||||||
```
|
```
|
||||||
DATABASE_URL=mysql://partdb:YOUR_SECRET_PASSWORD@127.0.0.1:3306/partdb
|
DATABASE_URL=DATABASE_URL=mysql://partdb:YOUR_SECRET_PASSWORD@127.0.0.1:3306/partdb
|
||||||
```
|
```
|
||||||
|
|
||||||
5. Create the database schema with:
|
5. Create the database schema with:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
sudo -u www-data php bin/console doctrine:migrations:migrate
|
sudo -u www-data php bin/console doctrine:migrations:migrate
|
||||||
```
|
```
|
||||||
|
|
||||||
6. The migration step should have shown you a password for the admin user, which you can use now to log in to Part-DB.
|
6. The migration step should have shown you a password for the admin user, which you can use now to login to Part-DB.
|
||||||
|
|
|
@ -1,42 +0,0 @@
|
||||||
---
|
|
||||||
title: Kubernetes / Helm
|
|
||||||
layout: default
|
|
||||||
parent: Installation
|
|
||||||
nav_order: 5
|
|
||||||
---
|
|
||||||
|
|
||||||
# Kubernetes / Helm Charts
|
|
||||||
|
|
||||||
If you are using Kubernetes, you can use the [helm charts](https://helm.sh/) provided in this [repository](https://github.com/Part-DB/helm-charts).
|
|
||||||
|
|
||||||
## Usage
|
|
||||||
|
|
||||||
[Helm](https://helm.sh) must be installed to use the charts. Please refer to
|
|
||||||
Helm's [documentation](https://helm.sh/docs) to get started.
|
|
||||||
|
|
||||||
Once Helm has been set up correctly, add the repo as follows:
|
|
||||||
|
|
||||||
`helm repo add part-db https://part-db.github.io/helm-charts`
|
|
||||||
|
|
||||||
If you had already added this repo earlier, run `helm repo update` to retrieve
|
|
||||||
the latest versions of the packages. You can then run `helm search repo
|
|
||||||
part-db` to see the charts.
|
|
||||||
|
|
||||||
To install the part-db chart:
|
|
||||||
|
|
||||||
helm install my-part-db part-db/part-db
|
|
||||||
|
|
||||||
To uninstall the chart:
|
|
||||||
|
|
||||||
helm delete my-part-db
|
|
||||||
|
|
||||||
This repository is also available at [ArtifactHUB](https://artifacthub.io/packages/search?repo=part-db).
|
|
||||||
|
|
||||||
## Configuration
|
|
||||||
|
|
||||||
See the README in the [chart directory](https://github.com/Part-DB/helm-charts/tree/main/charts/part-db) for more
|
|
||||||
information on the available configuration options.
|
|
||||||
|
|
||||||
## Bugreports
|
|
||||||
|
|
||||||
If you find issues related to the helm charts, please open an issue in the [helm-charts repository](https://github.com/Part-DB/helm-charts).
|
|
|
@ -6,18 +6,14 @@ nav_order: 10
|
||||||
---
|
---
|
||||||
|
|
||||||
# Nginx
|
# Nginx
|
||||||
|
|
||||||
You can also use [nginx](https://www.nginx.com/) as webserver for Part-DB. Setup Part-DB with apache is a bit easier, so
|
You can also use [nginx](https://www.nginx.com/) as webserver for Part-DB. Setup Part-DB with apache is a bit easier, so
|
||||||
this is the method shown in the guides. This guide assumes that you already have a working nginx installation with PHP
|
this is the method shown in the guides. This guide assumes that you already have a working nginx installation with PHP
|
||||||
configured.
|
configured.
|
||||||
|
|
||||||
## Setup
|
## Setup
|
||||||
|
1. Install composer and yarn like described in the [apache guide]({% link installation/installation_guide-debian.md %}#install-composer).
|
||||||
1. Install composer and yarn as described in the [apache guide]({% link installation/installation_guide-debian.md
|
|
||||||
%}#install-composer).
|
|
||||||
2. Create a folder for Part-DB and install and configure it as described
|
2. Create a folder for Part-DB and install and configure it as described
|
||||||
3. Instead of creating the config for apache, add the following snippet to your nginx config:
|
3. Instead of creating the config for apache, add the following snippet to your nginx config:
|
||||||
|
|
||||||
```nginx
|
```nginx
|
||||||
server {
|
server {
|
||||||
# Redirect all HTTP requests to HTTPS
|
# Redirect all HTTP requests to HTTPS
|
||||||
|
@ -52,11 +48,6 @@ server {
|
||||||
location ~ \.php$ {
|
location ~ \.php$ {
|
||||||
return 404;
|
return 404;
|
||||||
}
|
}
|
||||||
|
|
||||||
# Set Content-Security-Policy for svg files, to block embedded javascript in there
|
|
||||||
location ~* \.svg$ {
|
|
||||||
add_header Content-Security-Policy "default-src 'self'; script-src 'none'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; frame-ancestors 'none';";
|
|
||||||
}
|
|
||||||
|
|
||||||
error_log /var/log/nginx/parts.error.log;
|
error_log /var/log/nginx/parts.error.log;
|
||||||
access_log /var/log/nginx/parts.access.log;
|
access_log /var/log/nginx/parts.access.log;
|
||||||
|
@ -73,6 +64,4 @@ server {
|
||||||
ssl_prefer_server_ciphers on;
|
ssl_prefer_server_ciphers on;
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
4. Restart nginx with `sudo systemctl restart nginx` and you should be able to access Part-DB under your configured domain.
|
||||||
4. Restart nginx with `sudo systemctl restart nginx` and you should be able to access Part-DB under your configured
|
|
||||||
domain.
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue