grocy / grocy-docker

ERP beyond your fridge - now containerized - this is the docker repo of https://github.com/grocy/grocy
MIT License
418 stars 120 forks source link

404 on node_modules following upgrade to 4.0.0 #217

Closed ms32035 closed 1 year ago

ms32035 commented 1 year ago

I just upgraded to the v4.0.0 image and now am getting a lot for 404 errors for urls like https://grocy.mydomain.com/node_modules/bootstrap-select/dist/js/bootstrap-select.min.js?v=4.0.0 or inside the continer

frontend_1  | 2023/07/29 22:21:37 [error] 9#9: *31 open() "/var/www/public/node_modules/summernote/dist/summernote-bs4.js" failed (2: No such file or directory), client: 192.168.192.3, server: _, request: "GET /node_modules/summernote/dist/summernote-bs4.js?v=4.0.0 HTTP/1.1",

Setting NODE_PATH didn't resolve the issue

jayaddison commented 1 year ago

Hi @ms32035 - thanks for reporting this. Could you check that both the frontend and backend containers were updated? Some of the resources in the frontend container have moved from a node_modules directory to a packages directory - this seems likely to be the source of the problem somehow.

ms32035 commented 1 year ago

Yes, both have been updated

jayaddison commented 1 year ago

Ok, thanks :+1:

Hmm. One other possibility: do you have users who have browser sessions open against the previous instance of the Grocy application? If so, they could be a source of requests to the deprecated node_modules URL paths that no longer exist after the upgrade of the frontend container.

ms32035 commented 1 year ago

it happens also on a new session in private mode

jayaddison commented 1 year ago

Ok, hm. I believe you, although I have so far been unable to replicate this using a fresh grocy-docker 4.0.0 frontend + backend pairing.

Do you have any customised JavaScript modules enabled in this deployment?

ms32035 commented 1 year ago

Nothing like that

jayaddison commented 1 year ago

Are you familiar with the developer tools in your browser? (Firefox calls this "Web Developer Tools", and Chrome - and I think Edge - have similar functionality too)

The reason I ask is that the next step could be to figure out what part of the page/app these node_modules requests are originating from.

ms32035 commented 1 year ago

yep, what do you exactly need?

image

jayaddison commented 1 year ago

Great, thanks. If you view the details for one of the requests that experienced an HTTP 404 response status, is there information about the request to indicate where it originated from? (for example: an HTML element, JavaScript function, ...)

ms32035 commented 1 year ago

anonymised details tab

Request URL:
https://grocy.mydomain.com/node_modules/gettext-translator/src/translator.js?v=4.0.0
Request Method:
GET
Status Code:
404
Remote Address:
1.2.3.4:443
Referrer Policy:
strict-origin-when-cross-origin
Content-Encoding:
gzip
Content-Type:
text/html; charset=utf-8
Date:
Sun, 30 Jul 2023 08:43:31 GMT
Server:
nginx/1.24.0
:authority:
grocy.mydomain.com
:method:
GET
:path:
/node_modules/gettext-translator/src/translator.js?v=4.0.0
:scheme:
https
Accept:
*/*
Accept-Encoding:
gzip, deflate, br
Accept-Language:
en-GB,en;q=0.9
Dnt:
1
Referer:
https://grocy.mydomain.com/login
Sec-Ch-Ua:
"Not/A)Brand";v="99", "Microsoft Edge";v="115", "Chromium";v="115"
Sec-Ch-Ua-Mobile:
?0
Sec-Ch-Ua-Platform:
"Windows"
Sec-Fetch-Dest:
script
Sec-Fetch-Mode:
no-cors
Sec-Fetch-Site:
same-origin
User-Agent:
Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/115.0.0.0 Safari/537.36 Edg/115.0.1901.188
jayaddison commented 1 year ago

Ok, thanks again. And -- while logged out -- could you paste the HTML source of the /login page -- either here, or as a GitHub gist?

ms32035 commented 1 year ago

<!DOCTYPE html>
<html lang="en"
    dir="ltr">

<head>
    <meta charset="utf-8">
    <meta http-equiv="x-ua-compatible"
        content="ie=edge">
    <meta name="viewport"
        content="width=device-width, initial-scale=1, shrink-to-fit=no">

    <meta name="robots"
        content="noindex,nofollow">
    <meta name="format-detection"
        content="telephone=no">

    <meta name="author"
        content="Bernd Bestel (bernd@berrnd.de)">

    <link rel="apple-touch-icon"
        sizes="180x180"
        href="https://grocy.mydomain.com/img/appicons/apple-touch-icon.png?v=4.0.0">
    <link rel="icon"
        type="image/png"
        sizes="32x32"
        href="https://grocy.mydomain.com/img/appicons/favicon-32x32.png?v=4.0.0">
    <link rel="icon"
        type="image/png"
        sizes="16x16"
        href="https://grocy.mydomain.com/img/appicons/favicon-16x16.png?v=4.0.0">
    <link rel="manifest"
        href="https://grocy.mydomain.com/img/appicons/site.webmanifest?v=4.0.0">
    <link rel="mask-icon"
        href="https://grocy.mydomain.com/img/appicons/safari-pinned-tab.svg?v=4.0.0"
        color="#0b024c">
    <link rel="shortcut icon"
        href="https://grocy.mydomain.com/img/appicons/favicon.ico?v=4.0.0">
    <meta name="apple-mobile-web-app-title"
        content="grocy">
    <meta name="application-name"
        content="grocy">
    <meta name="msapplication-TileColor"
        content="#e5e5e5">
    <meta name="msapplication-config"
        content="https://grocy.mydomain.com/img/appicons/browserconfig.xml?v=4.0.0">
    <meta name="theme-color"
        content="#ffffff">

    <title>Login | grocy</title>
    <link href="https://grocy.mydomain.com/node_modules/bootstrap/dist/css/bootstrap.min.css?v=4.0.0"
        rel="stylesheet">
    <link href="https://grocy.mydomain.com/node_modules/startbootstrap-sb-admin/css/sb-admin.min.css?v=4.0.0"
        rel="stylesheet">
    <link href="https://grocy.mydomain.com/node_modules/@fortawesome/fontawesome-free/css/all.min.css?v=4.0.0"
        rel="stylesheet">
    <link href="https://grocy.mydomain.com/node_modules/@danielfarrell/bootstrap-combobox/css/bootstrap-combobox.css?v=4.0.0"
        rel="stylesheet">
    <link href="https://grocy.mydomain.com/node_modules/datatables.net-bs4/css/dataTables.bootstrap4.min.css?v=4.0.0"
        rel="stylesheet">
    <link href="https://grocy.mydomain.com/node_modules/datatables.net-colreorder-bs4/css/colReorder.bootstrap4.min.css?v=4.0.0"
        rel="stylesheet">
    <link href="https://grocy.mydomain.com/node_modules/datatables.net-rowgroup-bs4/css/rowGroup.bootstrap4.min.css?v=4.0.0"
        rel="stylesheet">
    <link href="https://grocy.mydomain.com/node_modules/datatables.net-select-bs4/css/select.bootstrap4.min.css?v=4.0.0"
        rel="stylesheet">
    <link href="https://grocy.mydomain.com/node_modules/toastr/build/toastr.min.css?v=4.0.0"
        rel="stylesheet">
    <link href="https://grocy.mydomain.com/node_modules/tempusdominus-bootstrap-4/build/css/tempusdominus-bootstrap-4.min.css?v=4.0.0"
        rel="stylesheet">
    <link href="https://grocy.mydomain.com/node_modules/summernote/dist/summernote-bs4.css?v=4.0.0"
        rel="stylesheet">
    <link href="https://grocy.mydomain.com/node_modules/bootstrap-select/dist/css/bootstrap-select.min.css?v=4.0.0"
        rel="stylesheet">
    <link href="https://grocy.mydomain.com/node_modules/@fontsource/noto-sans/latin.css?v=4.0.0"
        rel="stylesheet">
    <link href="https://grocy.mydomain.com/css/grocy.css?v=4.0.0"
        rel="stylesheet">

        <script>
        var Grocy = { };
        Grocy.Components = { };
        Grocy.Mode = 'production';
        Grocy.BaseUrl = 'https://grocy.mydomain.com/';
        Grocy.CurrentUrlRelative = "/" + window.location.href.split('?')[0].replace(Grocy.BaseUrl, "");
        Grocy.ActiveNav = '';
        Grocy.Currency = 'GBP';
        Grocy.CalendarFirstDayOfWeek = '';
        Grocy.CalendarShowWeekNumbers = true;
        Grocy.LocalizationStrings = {"domain":"grocy\/strings","plural-forms":null,"messages":{"":{"Stock overview":["Stock overview"],"%s products expiring within the next %s days":["%s products expiring within the next %s days"],"%s products are already expired":["%s products are already expired"],"%s products are below defined min. stock amount":["%s products are below defined min. stock amount"],"Product":["Product"],"Amount":["Amount"],"Next best before date":["Next best before date"],"Logout":["Logout"],"Chores overview":["Chores overview"],"Batteries overview":["Batteries overview"],"Purchase":["Purchase"],"Consume":["Consume"],"Inventory":["Inventory"],"Shopping list":["Shopping list"],"Chore tracking":["Chore tracking"],"Battery tracking":["Battery tracking"],"Products":["Products"],"Locations":["Locations"],"Shopping locations":["Shopping locations"],"Quantity units":["Quantity units"],"Chores":["Chores"],"Batteries":["Batteries"],"Chore":["Chore"],"Next estimated tracking":["Next estimated tracking"],"Last tracked":["Last tracked"],"Battery":["Battery"],"Last charged":["Last charged"],"Next planned charge cycle":["Next planned charge cycle"],"Best before":["Best before"],"OK":["OK"],"Product overview":["Product overview"],"Stock quantity unit":["Stock quantity unit"],"Stock amount":["Stock amount"],"Last purchased":["Last purchased"],"Last used":["Last used"],"Spoiled":["Spoiled"],"Barcode lookup is disabled":["Barcode lookup is disabled"],"will be added to the list of barcodes for the selected product on submit":["will be added to the list of barcodes for the selected product on submit"],"New amount":["New amount"],"Note":["Note"],"Tracked time":["Tracked time"],"Chore overview":["Chore overview"],"Tracked count":["Tracked count"],"Battery overview":["Battery overview"],"Charge cycles count":["Charge cycles count"],"Create shopping list item":["Create shopping list item"],"Edit shopping list item":["Edit shopping list item"],"Save":["Save"],"Add":["Add"],"Name":["Name"],"Location":["Location"],"Shopping location":["Shopping location"],"Min. stock amount":["Min. stock amount"],"QU purchase":["QU purchase"],"QU stock":["QU stock"],"QU factor":["QU factor"],"Description":["Description"],"Create product":["Create product"],"Barcode(s)":["Barcode(s)"],"Minimum stock amount":["Minimum stock amount"],"Default best before days":["Default best before days"],"Quantity unit purchase":["Quantity unit purchase"],"Quantity unit stock":["Quantity unit stock"],"Factor purchase to stock quantity unit":["Factor purchase to stock quantity unit"],"Create location":["Create location"],"Create shopping location":["Create shopping location"],"Create quantity unit":["Create quantity unit"],"Period type":["Period type"],"Period days":["Period days"],"Create chore":["Create chore"],"Used in":["Used in"],"Create battery":["Create battery"],"Edit battery":["Edit battery"],"Edit chore":["Edit chore"],"Edit quantity unit":["Edit quantity unit"],"Edit product":["Edit product"],"Edit location":["Edit location"],"Edit shopping location":["Edit shopping location"],"Record data":["Record data"],"Manage master data":["Manage master data"],"This will apply to added products":["This will apply to added products"],"never":["never"],"Add products that are below defined min. stock amount":["Add products that are below defined min. stock amount"],"For purchases this amount of days will be added to today for the best before date suggestion":["For purchases this amount of days will be added to today for the best before date suggestion"],"This means 1 %s purchased will be converted into %s %s in stock":["This means 1 %s purchased will be converted into %s %s in stock"],"Login":["Login"],"Username":["Username"],"Password":["Password"],"Invalid credentials, please try again":["Invalid credentials, please try again"],"Are you sure to delete battery \"%s\"?":["Are you sure to delete battery \"%s\"?"],"Yes":["Yes"],"No":["No"],"Are you sure to delete chore \"%s\"?":["Are you sure to delete chore \"%s\"?"],"\"%s\" could not be resolved to a product, how do you want to proceed?":["\"%s\" could not be resolved to a product, how do you want to proceed?"],"Create or assign product":["Create or assign product"],"Cancel":["Cancel"],"Add as new product":["Add as new product"],"Add as barcode to existing product":["Add as barcode to existing product"],"Add as new product and prefill barcode":["Add as new product and prefill barcode"],"Are you sure to delete quantity unit \"%s\"?":["Are you sure to delete quantity unit \"%s\"?"],"Are you sure to delete product \"%s\"?":["Are you sure to delete product \"%s\"?"],"Are you sure to delete location \"%s\"?":["Are you sure to delete location \"%s\"?"],"Are you sure to delete shopping location \"%s\"?":["Are you sure to delete shopping location \"%s\"?"],"Manage API keys":["Manage API keys"],"REST API & data model documentation":["REST API & data model documentation"],"API keys":["API keys"],"Create new API key":["Create new API key"],"API key":["API key"],"Expires":["Expires"],"Created":["Created"],"This product is not in stock":["This product is not in stock"],"This means %s will be added to stock":["This means %s will be added to stock"],"This means %s will be removed from stock":["This means %s will be removed from stock"],"This means it is estimated that a new execution of this chore is tracked %s days after the last was tracked":["This means it is estimated that a new execution of this chore is tracked %s days after the last was tracked"],"Removed %s %s of %s from stock":["Removed %s %s of %s from stock"],"About grocy":["About grocy"],"Close":["Close"],"%s batteries are due to be charged within the next %s days":["%s batteries are due to be charged within the next %s days"],"%s batteries are overdue to be charged":["%s batteries are overdue to be charged"],"%s chores are due to be done within the next %s days":["%s chores are due to be done within the next %s days"],"%s chores are overdue to be done":["%s chores are overdue to be done"],"Released on":["Released on"],"Consume %s %s of %s":["Consume %s %s of %s"],"Added %s %s of %s to stock":["Added %s %s of %s to stock"],"Stock amount of %s is now %s %s":["Stock amount of %s is now %s %s"],"Tracked execution of chore %s on %s":["Tracked execution of chore %s on %s"],"Tracked charge cycle of battery %s on %s":["Tracked charge cycle of battery %s on %s"],"Consume all %s which are currently in stock":["Consume all %s which are currently in stock"],"All":["All"],"Track charge cycle of battery %s":["Track charge cycle of battery %s"],"Track execution of chore %s":["Track execution of chore %s"],"Filter by location":["Filter by location"],"Search":["Search"],"Not logged in":["Not logged in"],"You have to select a product":["You have to select a product"],"You have to select a chore":["You have to select a chore"],"You have to select a battery":["You have to select a battery"],"A name is required":["A name is required"],"A location is required":["A location is required"],"The amount cannot be lower than %s":["The amount cannot be lower than %s"],"This cannot be negative":["This cannot be negative"],"A quantity unit is required":["A quantity unit is required"],"A period type is required":["A period type is required"],"A best before date is required":["A best before date is required"],"Settings":["Settings"],"This can only be before now":["This can only be before now"],"Calendar":["Calendar"],"Recipes":["Recipes"],"Edit recipe":["Edit recipe"],"New recipe":["New recipe"],"Ingredients list":["Ingredients list"],"Add recipe ingredient":["Add recipe ingredient"],"Edit recipe ingredient":["Edit recipe ingredient"],"Are you sure to delete recipe \"%s\"?":["Are you sure to delete recipe \"%s\"?"],"Are you sure to delete recipe ingredient \"%s\"?":["Are you sure to delete recipe ingredient \"%s\"?"],"Are you sure to empty shopping list \"%s\"?":["Are you sure to empty shopping list \"%s\"?"],"Clear list":["Clear list"],"Requirements fulfilled":["Requirements fulfilled"],"Put missing products on shopping list":["Put missing products on shopping list"],"Not enough in stock, %s ingredients missing":["Not enough in stock, %s ingredients missing"],"Enough in stock":["Enough in stock"],"Not enough in stock, %s ingredients missing but already on the shopping list":["Not enough in stock, %s ingredients missing but already on the shopping list"],"Expand to fullscreen":["Expand to fullscreen"],"Ingredients":["Ingredients"],"Preparation":["Preparation"],"Recipe":["Recipe"],"Not enough in stock, %s missing, %s already on shopping list":["Not enough in stock, %s missing, %s already on shopping list"],"Show notes":["Show notes"],"Put missing amount on shopping list":["Put missing amount on shopping list"],"Are you sure to put all missing ingredients for recipe \"%s\" on the shopping list?":["Are you sure to put all missing ingredients for recipe \"%s\" on the shopping list?"],"Added for recipe %s":["Added for recipe %s"],"Manage users":["Manage users"],"User":["User"],"Users":["Users"],"Are you sure to delete user \"%s\"?":["Are you sure to delete user \"%s\"?"],"Create user":["Create user"],"Edit user":["Edit user"],"First name":["First name"],"Last name":["Last name"],"A username is required":["A username is required"],"Confirm password":["Confirm password"],"Passwords do not match":["Passwords do not match"],"Change password":["Change password"],"Done by":["Done by"],"Last done by":["Last done by"],"Unknown":["Unknown"],"Filter by chore":["Filter by chore"],"Chores journal":["Chores journal"],"0 means suggestions for the next charge cycle are disabled":["0 means suggestions for the next charge cycle are disabled"],"Charge cycle interval (days)":["Charge cycle interval (days)"],"Last price":["Last price"],"Price history":["Price history"],"No price history available":["No price history available"],"Price":["Price"],"in %s per purchase quantity unit":["in %s per purchase quantity unit"],"The price cannot be lower than %s":["The price cannot be lower than %s"],"%s product expires within the next %s days":["%s product expires within the next %s days"],"%s product is already expired":["%s product is already expired"],"%s product is below defined min. stock amount":["%s product is below defined min. stock amount"],"Unit":["Unit"],"Units":["Units"],"%s chore is due to be done within the next %s days":["%s chore is due to be done within the next %s days"],"%s chore is overdue to be done":["%s chore is overdue to be done"],"%s battery is due to be charged within the next %s days":["%s battery is due to be charged within the next %s days"],"%s battery is overdue to be charged":["%s battery is overdue to be charged"],"%s unit was automatically added and will apply in addition to the amount entered here":["%s unit was automatically added and will apply in addition to the amount entered here"],"in singular form":["in singular form"],"in plural form":["in plural form"],"Never expires":["Never expires"],"This cannot be lower than %s":["This cannot be lower than %s"],"-1 means that this product never expires":["-1 means that this product never expires"],"Quantity unit":["Quantity unit"],"Only check if a single unit is in stock (a different quantity can then be used above)":["Only check if a single unit is in stock (a different quantity can then be used above)"],"Are you sure to consume all ingredients needed by recipe \"%s\" (ingredients marked with \"check only if a single unit is in stock\" will be ignored)?":["Are you sure to consume all ingredients needed by recipe \"%s\" (ingredients marked with \"check only if a single unit is in stock\" will be ignored)?"],"Removed all ingredients of recipe \"%s\" from stock":["Removed all ingredients of recipe \"%s\" from stock"],"Consume all ingredients needed by this recipe":["Consume all ingredients needed by this recipe"],"Click to show technical details":["Click to show technical details"],"Error while saving, probably this item already exists":["Error while saving, probably this item already exists"],"Error details":["Error details"],"Tasks":["Tasks"],"Show done tasks":["Show done tasks"],"Task":["Task"],"Due":["Due"],"Assigned to":["Assigned to"],"Mark task \"%s\" as completed":["Mark task \"%s\" as completed"],"Uncategorized":["Uncategorized"],"Task categories":["Task categories"],"Create task":["Create task"],"A due date is required":["A due date is required"],"Category":["Category"],"Edit task":["Edit task"],"Are you sure to delete task \"%s\"?":["Are you sure to delete task \"%s\"?"],"%s task is due to be done within the next %s days":["%s task is due to be done within the next %s days"],"%s tasks are due to be done within the next %s days":["%s tasks are due to be done within the next %s days"],"%s task is overdue to be done":["%s task is overdue to be done"],"%s tasks are overdue to be done":["%s tasks are overdue to be done"],"Edit task category":["Edit task category"],"Create task category":["Create task category"],"Product groups":["Product groups"],"Ungrouped":["Ungrouped"],"Create product group":["Create product group"],"Edit product group":["Edit product group"],"Product group":["Product group"],"Are you sure to delete product group \"%s\"?":["Are you sure to delete product group \"%s\"?"],"Stay logged in permanently":["Stay logged in permanently"],"When not set, you will get logged out at latest after 30 days":["When not set, you will get logged out at latest after 30 days"],"Filter by status":["Filter by status"],"Below min. stock amount":["Below min. stock amount"],"Expiring soon":["Expiring soon"],"Already expired":["Already expired"],"Due soon":["Due soon"],"Overdue":["Overdue"],"View settings":["View settings"],"Auto reload on external changes":["Auto reload on external changes"],"Enable night mode":["Enable night mode"],"Auto enable in time range":["Auto enable in time range"],"From":["From"],"in format":["in format"],"To":["To"],"Time range goes over midnight":["Time range goes over midnight"],"Product picture":["Product picture"],"No file selected":["No file selected"],"If you don't select a file, the current picture will not be altered":["If you don't select a file, the current picture will not be altered"],"Delete":["Delete"],"The current picture will be deleted when you save the product":["The current picture will be deleted when you save the product"],"Select file":["Select file"],"Image of product %s":["Image of product %s"],"This product cannot be deleted because it is in stock, please remove the stock amount first.":["This product cannot be deleted because it is in stock, please remove the stock amount first."],"Delete not possible":["Delete not possible"],"Equipment":["Equipment"],"Instruction manual":["Instruction manual"],"The selected equipment has no instruction manual":["The selected equipment has no instruction manual"],"Notes":["Notes"],"Edit equipment":["Edit equipment"],"Create equipment":["Create equipment"],"If you don't select a file, the current instruction manual will not be altered":["If you don't select a file, the current instruction manual will not be altered"],"No instruction manual available":["No instruction manual available"],"The current instruction manual will be deleted when you save the equipment":["The current instruction manual will be deleted when you save the equipment"],"No picture available":["No picture available"],"Filter by product group":["Filter by product group"],"Presets for new products":["Presets for new products"],"Included recipes":["Included recipes"],"A recipe is required":["A recipe is required"],"Add included recipe":["Add included recipe"],"Edit included recipe":["Edit included recipe"],"Group":["Group"],"This will be used as a headline to group ingredients together":["This will be used as a headline to group ingredients together"],"Journal":["Journal"],"Stock journal":["Stock journal"],"Filter by product":["Filter by product"],"Booking time":["Booking time"],"Booking type":["Booking type"],"Undo booking":["Undo booking"],"Undone on":["Undone on"],"Batteries journal":["Batteries journal"],"Filter by battery":["Filter by battery"],"Undo charge cycle":["Undo charge cycle"],"Undo chore execution":["Undo chore execution"],"Chore execution successfully undone":["Chore execution successfully undone"],"Undo":["Undo"],"Booking successfully undone":["Booking successfully undone"],"Charge cycle successfully undone":["Charge cycle successfully undone"],"This cannot be negative and must be an integral number":["This cannot be negative and must be an integral number"],"Disable stock fulfillment checking for this ingredient":["Disable stock fulfillment checking for this ingredient"],"Add all list items to stock":["Add all list items to stock"],"Add %s %s of %s to stock":["Add %s %s of %s to stock"],"Adding shopping list item %s of %s":["Adding shopping list item %s of %s"],"Use a specific stock item":["Use a specific stock item"],"The first item in this list would be picked by the default rule which is \"First expiring first, then first in first out\"":["The first item in this list would be picked by the default rule which is \"First expiring first, then first in first out\""],"Mark %s %s of %s as open":["Mark %s %s of %s as open"],"When a product was marked as opened, the best before date will be replaced by today + this amount of days (a value of 0 disables this)":["When a product was marked as opened, the best before date will be replaced by today + this amount of days (a value of 0 disables this)"],"Default best before days after opened":["Default best before days after opened"],"Marked %s %s of %s as opened":["Marked %s %s of %s as opened"],"Mark as opened":["Mark as opened"],"Expires on %s; Bought on %s":["Expires on %s; Bought on %s"],"Not opened":["Not opened"],"Opened":["Opened"],"%s opened":["%s opened"],"Product expires":["Product expires"],"Task due":["Task due"],"Chore due":["Chore due"],"Battery charge cycle due":["Battery charge cycle due"],"Show clock in header":["Show clock in header"],"Stock settings":["Stock settings"],"Shopping list to stock workflow":["Shopping list to stock workflow"],"Automatically do the booking using the last price and the amount of the shopping list item, if the product has \"Default best before days\" set":["Automatically do the booking using the last price and the amount of the shopping list item, if the product has \"Default best before days\" set"],"Skip":["Skip"],"Servings":["Servings"],"Costs":["Costs"],"Based on the prices of the last purchase per product":["Based on the prices of the last purchase per product"],"The ingredients listed here result in this amount of servings":["The ingredients listed here result in this amount of servings"],"Do not check against the shopping list when adding missing items to it":["Do not check against the shopping list when adding missing items to it"],"By default the amount to be added to the shopping list is \"needed amount - stock amount - shopping list amount\" - when this is enabled, it is only checked against the stock amount, not against what is already on the shopping list":["By default the amount to be added to the shopping list is \"needed amount - stock amount - shopping list amount\" - when this is enabled, it is only checked against the stock amount, not against what is already on the shopping list"],"Picture":["Picture"],"Uncheck ingredients to not put them on the shopping list":["Uncheck ingredients to not put them on the shopping list"],"This is for statistical purposes only":["This is for statistical purposes only"],"You have to select a recipe":["You have to select a recipe"],"Key type":["Key type"],"Share\/Integrate calendar (iCal)":["Share\/Integrate calendar (iCal)"],"Use the following (public) URL to share or integrate the calendar in iCal format":["Use the following (public) URL to share or integrate the calendar in iCal format"],"Allow partial units in stock":["Allow partial units in stock"],"Enable tare weight handling":["Enable tare weight handling"],"This is useful e.g. for flour in jars - on purchase\/consume\/inventory you always weigh the whole jar, the amount to be posted is then automatically calculated based on what is in stock and the tare weight defined below":["This is useful e.g. for flour in jars - on purchase\/consume\/inventory you always weigh the whole jar, the amount to be posted is then automatically calculated based on what is in stock and the tare weight defined below"],"Tare weight":["Tare weight"],"Tare weight handling enabled - please weigh the whole container, the amount to be posted will be automatically calculcated":["Tare weight handling enabled - please weigh the whole container, the amount to be posted will be automatically calculcated"],"You have to select a location":["You have to select a location"],"You have to select a shopping location":["You have to select a shopping location"],"List":["List"],"Gallery":["Gallery"],"The current picture will be deleted when you save the recipe":["The current picture will be deleted when you save the recipe"],"Show product details":["Show product details"],"Stock journal for this product":["Stock journal for this product"],"Show chore details":["Show chore details"],"Journal for this chore":["Journal for this chore"],"Show battery details":["Show battery details"],"Journal for this battery":["Journal for this battery"],"System info":["System info"],"Changelog":["Changelog"],"will be multiplied a factor of %s to get %s":["will be multiplied a factor of %s to get %s"],"The given date is earlier than today, are you sure?":["The given date is earlier than today, are you sure?"],"Product count":["Product count"],"Type a new product name or barcode and hit TAB to start a workflow":["Type a new product name or barcode and hit TAB to start a workflow"],"This will be used as the default setting when adding this product as a recipe ingredient":["This will be used as the default setting when adding this product as a recipe ingredient"],"Add item":["Add item"],"Selected shopping list":["Selected shopping list"],"New shopping list":["New shopping list"],"Delete shopping list":["Delete shopping list"],"Chores settings":["Chores settings"],"Batteries settings":["Batteries settings"],"Tasks settings":["Tasks settings"],"Create shopping list":["Create shopping list"],"Are you sure to delete shopping list \"%s\"?":["Are you sure to delete shopping list \"%s\"?"],"Average shelf life":["Average shelf life"],"Spoil rate":["Spoil rate"],"Show more":["Show more"],"Show less":["Show less"],"The amount must be between %s and %s":["The amount must be between %s and %s"],"Day of month":["Day of month"],"Monday":["Monday"],"Tuesday":["Tuesday"],"Wednesday":["Wednesday"],"Thursday":["Thursday"],"Friday":["Friday"],"Saturday":["Saturday"],"Sunday":["Sunday"],"Configure userfields":["Configure userfields"],"Userfields":["Userfields"],"Filter by entity":["Filter by entity"],"Entity":["Entity"],"Caption":["Caption"],"Type":["Type"],"Create userfield":["Create userfield"],"A entity is required":["A entity is required"],"A caption is required":["A caption is required"],"A type is required":["A type is required"],"Show as column in tables":["Show as column in tables"],"This is required and can only contain letters and numbers":["This is required and can only contain letters and numbers"],"Edit userfield":["Edit userfield"],"no-assignment":["No assignment"],"who-least-did-first":["Who least did first"],"random":["Random"],"in-alphabetical-order":["In alphabetical order"],"timeago_locale":["en"],"timeago_nan":["NaN years ago"],"moment_locale":["x"],"datatables_localization":["{\"sEmptyTable\":\"No data available in table\",\"sInfo\":\"Showing _START_ to _END_ of _TOTAL_ entries\",\"sInfoEmpty\":\"Showing 0 to 0 of 0 entries\",\"sInfoFiltered\":\"(filtered from _MAX_ total entries)\",\"sInfoPostFix\":\"\",\"sInfoThousands\":\",\",\"sLengthMenu\":\"Show _MENU_ entries\",\"sLoadingRecords\":\"Loading...\",\"sProcessing\":\"Processing...\",\"sSearch\":\"Search:\",\"sZeroRecords\":\"No matching records found\",\"oPaginate\":{\"sFirst\":\"First\",\"sLast\":\"Last\",\"sNext\":\"Next\",\"sPrevious\":\"Previous\"},\"oAria\":{\"sSortAscending\":\": activate to sort column ascending\",\"sSortDescending\":\": activate to sort column descending\"}}"],"summernote_locale":["x"],"fullcalendar_locale":["x"],"bootstrap-select_locale":["x"],"purchase":["Purchase"],"transfer_to":["Transfer To"],"transfer_from":["Transfer From"],"consume":["Consume"],"inventory-correction":["Inventory correction"],"product-opened":["Product opened"],"stock-edit-old":["Stock entry edited (old values)"],"stock-edit-new":["Stock entry edited (new values)"],"self-production":["Self-production"],"manually":["Manually"],"daily":["Daily"],"weekly":["Weekly"],"monthly":["Monthly"],"yearly":["Yearly"],"hourly":["Hourly"],"adaptive":["Adaptive"],"text-single-line":["Text (single line)"],"text-multi-line":["Text (multi line)"],"number-integral":["Number (integral)"],"number-decimal":["Number (decimal)"],"date":["Date (without time)"],"datetime":["Date & time"],"checkbox":["Checkbox"],"preset-list":["Select list (a single item can be selected)"],"preset-checklist":["Select list (multiple items can be selected)"],"link":["Link"],"link-with-title":["Link (with title)"],"file":["File"],"image":["Image"],"ADMIN":["All permissions"],"USERS_CREATE":["Create users"],"USERS_EDIT":["Edit users (including passwords)"],"USERS_READ":["Show users"],"USERS_EDIT_SELF":["Edit own user data \/ change own password"],"BATTERIES_UNDO_CHARGE_CYCLE":["Undo charge cycle"],"BATTERIES_TRACK_CHARGE_CYCLE":["Track charge cycle"],"CHORE_TRACK_EXECUTION":["Track execution"],"CHORE_UNDO_EXECUTION":["Undo execution"],"MASTER_DATA_EDIT":["Edit master data"],"TASKS_UNDO_EXECUTION":["Undo execution"],"TASKS_MARK_COMPLETED":["Mark completed"],"STOCK_EDIT":["Edit stock entries"],"STOCK_TRANSFER":["Transfer"],"STOCK_INVENTORY":["Inventory"],"STOCK_CONSUME":["Consume"],"STOCK_OPEN":["Open products"],"STOCK_PURCHASE":["Purchase"],"SHOPPINGLIST_ITEMS_ADD":["Add items"],"SHOPPINGLIST_ITEMS_DELETE":["Remove items"],"USERS":["User management"],"STOCK":["Stock"],"SHOPPINGLIST":["Shopping list"],"CHORES":["Chores"],"BATTERIES":["Batteries"],"TASKS":["Tasks"],"RECIPES":["Recipes"],"EQUIPMENT":["Equipment"],"CALENDAR":["Calendar"],"RECIPES_MEALPLAN":["Meal plan"],"cs":["Czech"],"da":["Danish"],"de":["German"],"el_GR":["Greek"],"en":["English"],"en_GB":["English (Great Britain)"],"es":["Spanish"],"fr":["French"],"hu":["Hungarian"],"it":["Italian"],"ja":["Japanese"],"ko_KR":["Korean"],"nl":["Dutch"],"no":["Norwegian"],"pl":["Polish"],"pt_BR":["Portuguese (Brazil)"],"pt_PT":["Portuguese (Portugal)"],"ru":["Russian"],"sk_SK":["Slovak"],"sl":["Slovenian"],"sv_SE":["Swedish"],"tr":["Turkish"],"zh_TW":["Chinese (Taiwan)"],"zh_CN":["Chinese (China)"],"he_IL":["Hebrew (Israel)"],"ta":["Tamil"],"fi":["Finnish"],"ca":["Catalan"]}}};
        Grocy.LocalizationStringsQu = {"domain":null,"plural-forms":"","messages":{"":{"Piece":["Piece"],"Pack":["Pack"]}}};
        Grocy.FeatureFlags = {"GROCY_FEATURE_FLAG_STOCK":true,"GROCY_FEATURE_FLAG_SHOPPINGLIST":true,"GROCY_FEATURE_FLAG_RECIPES":true,"GROCY_FEATURE_FLAG_CHORES":true,"GROCY_FEATURE_FLAG_TASKS":true,"GROCY_FEATURE_FLAG_BATTERIES":true,"GROCY_FEATURE_FLAG_EQUIPMENT":true,"GROCY_FEATURE_FLAG_CALENDAR":true,"GROCY_FEATURE_FLAG_LABEL_PRINTER":false,"GROCY_FEATURE_FLAG_STOCK_PRICE_TRACKING":true,"GROCY_FEATURE_FLAG_STOCK_LOCATION_TRACKING":true,"GROCY_FEATURE_FLAG_STOCK_BEST_BEFORE_DATE_TRACKING":true,"GROCY_FEATURE_FLAG_STOCK_PRODUCT_OPENED_TRACKING":true,"GROCY_FEATURE_FLAG_STOCK_PRODUCT_FREEZING":true,"GROCY_FEATURE_FLAG_STOCK_BEST_BEFORE_DATE_FIELD_NUMBER_PAD":true,"GROCY_FEATURE_FLAG_SHOPPINGLIST_MULTIPLE_LISTS":true,"GROCY_FEATURE_FLAG_CHORES_ASSIGNMENTS":true,"GROCY_FEATURE_FLAG_THERMAL_PRINTER":false,"GROCY_FEATURE_FLAG_AUTO_TORCH_ON_WITH_CAMERA":true,"GROCY_FEATURE_FLAG_RECIPES_MEALPLAN":true,"GROCY_FEATURE_FLAG_DISABLE_BROWSER_BARCODE_CAMERA_SCANNING":false};
        Grocy.Webhooks = {
                };

                Grocy.UserSettings = { };
        Grocy.UserId = -1;
            </script>
</head>

<body class="fixed-nav  ">
        <nav id="mainNav"
        class="navbar navbar-expand-lg navbar-light fixed-top">
        <a class="navbar-brand py-0"
            href="https://grocy.mydomain.com/"><img src="https://grocy.mydomain.com/img/grocy_logo.svg?v=4.0.0"
                height="30"></a>
        <span id="clock-container"
            class="text-muted font-italic d-none">
            <i class="fa-regular fa-clock"></i>
            <span id="clock-small"
                class="d-inline d-sm-none"></span>
            <span id="clock-big"
                class="d-none d-sm-inline"></span>
        </span>

            </nav>

    <div class=" pt-0">
        <div class="container-fluid ">
            <div class="row mb-3">
                <div id="page-content"
                    class="col content-text">
                    <div class="row">
    <div class="col-lg-4 offset-lg-4 col-md-6 offset-md-3 col-12">
        <h2 class="text-center">Login</h2>

        <hr class="my-2">

        <form method="post"
            action="https://grocy.mydomain.com/login"
            id="login-form"
            novalidate>

            <div class="form-group">
                <label for="name">Username</label>
                <input type="text"
                    class="form-control"
                    required
                    id="username"
                    name="username">
            </div>

            <div class="form-group">
                <label for="name">Password</label>
                <input type="password"
                    class="form-control"
                    required
                    id="password"
                    name="password">
                <div id="login-error"
                    class="form-text text-danger d-none"></div>
            </div>

            <div class="form-group mt-n2">
                <div class="custom-control custom-checkbox">
                    <input type="checkbox"
                        class="form-check-input custom-control-input"
                        id="stay_logged_in"
                        name="stay_logged_in">
                    <label class="form-check-label custom-control-label"
                        for="stay_logged_in">
                        Stay logged in permanently
                        <i class="fa-solid fa-question-circle text-muted"
                            data-toggle="tooltip"
                            data-trigger="hover click"
                            title="When not set, you will get logged out at latest after 30 days"></i>
                    </label>
                </div>
            </div>

            <button id="login-button"
                class="btn btn-success">OK</button>

        </form>
    </div>
</div>
                </div>
            </div>
        </div>
    </div>

    <script src="https://grocy.mydomain.com/node_modules/jquery/dist/jquery.min.js?v=4.0.0"></script>
    <script src="https://grocy.mydomain.com/node_modules/bootstrap/dist/js/bootstrap.bundle.min.js?v=4.0.0"></script>
    <script src="https://grocy.mydomain.com/node_modules/startbootstrap-sb-admin/js/sb-admin.min.js?v=4.0.0"></script>
    <script src="https://grocy.mydomain.com/node_modules/bootbox/dist/bootbox.min.js?v=4.0.0"></script>
    <script src="https://grocy.mydomain.com/node_modules/jquery-serializejson/jquery.serializejson.min.js?v=4.0.0"></script>
    <script src="https://grocy.mydomain.com/node_modules/moment/min/moment.min.js?v=4.0.0"></script>
        <script src="https://grocy.mydomain.com/node_modules/@danielfarrell/bootstrap-combobox/js/bootstrap-combobox.js?v=4.0.0"></script>
    <script src="https://grocy.mydomain.com/node_modules/datatables.net/js/jquery.dataTables.min.js?v=4.0.0"></script>
    <script src="https://grocy.mydomain.com/node_modules/datatables.net-bs4/js/dataTables.bootstrap4.js?v=4.0.0"></script>
    <script src="https://grocy.mydomain.com/node_modules/datatables.net-colreorder/js/dataTables.colReorder.min.js?v=4.0.0"></script>
    <script src="https://grocy.mydomain.com/node_modules/datatables.net-colreorder-bs4/js/colReorder.bootstrap4.min.js?v=4.0.0"></script>
    <script src="https://grocy.mydomain.com/node_modules/datatables.net-plugins/filtering/type-based/accent-neutralise.js?v=4.0.0"></script>
    <script src="https://grocy.mydomain.com/node_modules/datatables.net-plugins/sorting/chinese-string.js?v=4.0.0"></script>
    <script src="https://grocy.mydomain.com/node_modules/datatables.net-rowgroup/js/dataTables.rowGroup.min.js?v=4.0.0"></script>
    <script src="https://grocy.mydomain.com/node_modules/datatables.net-rowgroup-bs4/js/rowGroup.bootstrap4.min.js?v=4.0.0"></script>
    <script src="https://grocy.mydomain.com/node_modules/datatables.net-select/js/dataTables.select.min.js?v=4.0.0"></script>
    <script src="https://grocy.mydomain.com/node_modules/datatables.net-select-bs4/js/select.bootstrap4.min.js?v=4.0.0"></script>
    <script src="https://grocy.mydomain.com/node_modules/toastr/build/toastr.min.js?v=4.0.0"></script>
    <script src="https://grocy.mydomain.com/node_modules/tempusdominus-bootstrap-4/build/js/tempusdominus-bootstrap-4.min.js?v=4.0.0"></script>
    <script src="https://grocy.mydomain.com/node_modules/sprintf-js/dist/sprintf.min.js?v=4.0.0"></script>
    <script src="https://grocy.mydomain.com/node_modules/gettext-translator/src/translator.js?v=4.0.0"></script>
    <script src="https://grocy.mydomain.com/node_modules/summernote/dist/summernote-bs4.js?v=4.0.0"></script>
        <script src="https://grocy.mydomain.com/node_modules/bootstrap-select/dist/js/bootstrap-select.min.js?v=4.0.0"></script>
        <script src="https://grocy.mydomain.com/node_modules/jquery-lazy/jquery.lazy.min.js?v=4.0.0"></script>
    <script src="https://grocy.mydomain.com/node_modules/nosleep.js/dist/NoSleep.min.js?v=4.0.0"></script>

    <script src="https://grocy.mydomain.com/js/extensions.js?v=4.0.0"></script>
    <script src="https://grocy.mydomain.com/js/grocy.js?v=4.0.0"></script>
    <script src="https://grocy.mydomain.com/js/grocy_dbchangedhandling.js?v=4.0.0"></script>
    <script src="https://grocy.mydomain.com/js/grocy_wakelockhandling.js?v=4.0.0"></script>
    <script src="https://grocy.mydomain.com/js/grocy_nightmode.js?v=4.0.0"></script>
    <script src="https://grocy.mydomain.com/js/grocy_clock.js?v=4.0.0"></script>
            <script src="https://grocy.mydomain.com/viewjs/login.js?v=4.0.0"></script>
    </body>

</html>
jayaddison commented 1 year ago
<!DOCTYPE html>
<html lang="en"
        dir="ltr">

<head>
        <meta charset="utf-8">
        <meta name="viewport"
                content="width=device-width, initial-scale=1">
        <meta name="robots"
                content="noindex,nofollow">

        <link rel="icon"
                type="image/png"
                sizes="32x32"
                href="http://127.0.0.1:8080/img/icon-32.png?v=4.0.0">
        <link rel="manifest"
                href="http://127.0.0.1:8080/manifest.json?v=4.0.0">

        <title>Login | Grocy</title>

        <link href="http://127.0.0.1:8080/packages/@fontsource/open-sans/latin.css?v=4.0.0"
                rel="stylesheet">
        <link href="http://127.0.0.1:8080/packages/bootstrap/dist/css/bootstrap.min.css?v=4.0.0"
                rel="stylesheet">
        <link href="http://127.0.0.1:8080/packages/@fortawesome/fontawesome-free/css/all.min.css?v=4.0.0"
                rel="stylesheet">
        <link href="http://127.0.0.1:8080/packages/toastr/build/toastr.min.css?v=4.0.0"
                rel="stylesheet">

        <link href="http://127.0.0.1:8080/css/grocy_menu_layout.css?v=4.0.0"
                rel="stylesheet">
        <link href="http://127.0.0.1:8080/css/grocy.css?v=4.0.0"
                rel="stylesheet">

                <script>
                var Grocy = { };
                Grocy.Components = { };
                Grocy.Mode = 'production';
                Grocy.BaseUrl = 'http://127.0.0.1:8080/';
                Grocy.CurrentUrlRelative = "/" + window.location.href.split('?')[0].replace(Grocy.BaseUrl, "");
                Grocy.View = 'login';
                Grocy.Currency = 'USD';
                Grocy.EnergyUnit = 'kcal';
                Grocy.CalendarFirstDayOfWeek = '';
                Grocy.CalendarShowWeekNumbers = true;
                Grocy.LocalizationStrings = {"domain":"grocy\/strings","plural-forms":null,"messages":{"":{"Stock overview":["Stock overview"],"%s products expiring within the next %s days":["%s products expiring within the next %s days"],"%s products are already expired":["%s products are already expired"],"%s products are below defined min. stock amount":["%s products are below defined min. stock amount"],"Product":["Product"],"Amount":["Amount"],"Next best before date":["Next best before date"],"Logout":["Logout"],"Chores overview":["Chores overview"],"Batteries overview":["Batteries overview"],"Purchase":["Purchase"],"Consume":["Consume"],"Inventory":["Inventory"],"Shopping list":["Shopping list"],"Chore tracking":["Chore tracking"],"Battery tracking":["Battery tracking"],"Products":["Products"],"Locations":["Locations"],"Shopping locations":["Shopping locations"],"Quantity units":["Quantity units"],"Chores":["Chores"],"Batteries":["Batteries"],"Chore":["Chore"],"Next estimated tracking":["Next estimated tracking"],"Last tracked":["Last tracked"],"Battery":["Battery"],"Last charged":["Last charged"],"Next planned charge cycle":["Next planned charge cycle"],"Best before":["Best before"],"OK":["OK"],"Product overview":["Product overview"],"Stock quantity unit":["Stock quantity unit"],"Stock amount":["Stock amount"],"Last purchased":["Last purchased"],"Last used":["Last used"],"Spoiled":["Spoiled"],"Barcode lookup is disabled":["Barcode lookup is disabled"],"will be added to the list of barcodes for the selected product on submit":["will be added to the list of barcodes for the selected product on submit"],"New amount":["New amount"],"Note":["Note"],"Tracked time":["Tracked time"],"Chore overview":["Chore overview"],"Tracked count":["Tracked count"],"Battery overview":["Battery overview"],"Charge cycles count":["Charge cycles count"],"Create shopping list item":["Create shopping list item"],"Edit shopping list item":["Edit shopping list item"],"Save":["Save"],"Add":["Add"],"Name":["Name"],"Location":["Location"],"Shopping location":["Shopping location"],"Min. stock amount":["Min. stock amount"],"QU purchase":["QU purchase"],"QU stock":["QU stock"],"QU factor":["QU factor"],"Description":["Description"],"Create product":["Create product"],"Barcode(s)":["Barcode(s)"],"Minimum stock amount":["Minimum stock amount"],"Default best before days":["Default best before days"],"Quantity unit purchase":["Quantity unit purchase"],"Quantity unit stock":["Quantity unit stock"],"Factor purchase to stock quantity unit":["Factor purchase to stock quantity unit"],"Create location":["Create location"],"Create shopping location":["Create shopping location"],"Create quantity unit":["Create quantity unit"],"Period type":["Period type"],"Period days":["Period days"],"Create chore":["Create chore"],"Used in":["Used in"],"Create battery":["Create battery"],"Edit battery":["Edit battery"],"Edit chore":["Edit chore"],"Edit quantity unit":["Edit quantity unit"],"Edit product":["Edit product"],"Edit location":["Edit location"],"Edit shopping location":["Edit shopping location"],"Record data":["Record data"],"Manage master data":["Manage master data"],"This will apply to added products":["This will apply to added products"],"never":["never"],"Add products that are below defined min. stock amount":["Add products that are below defined min. stock amount"],"For purchases this amount of days will be added to today for the best before date suggestion":["For purchases this amount of days will be added to today for the best before date suggestion"],"This means 1 %s purchased will be converted into %s %s in stock":["This means 1 %s purchased will be converted into %s %s in stock"],"Login":["Login"],"Username":["Username"],"Password":["Password"],"Invalid credentials, please try again":["Invalid credentials, please try again"],"Are you sure to delete battery \"%s\"?":["Are you sure to delete battery \"%s\"?"],"Yes":["Yes"],"No":["No"],"Are you sure to delete chore \"%s\"?":["Are you sure to delete chore \"%s\"?"],"\"%s\" could not be resolved to a product, how do you want to proceed?":["\"%s\" could not be resolved to a product, how do you want to proceed?"],"Create or assign product":["Create or assign product"],"Cancel":["Cancel"],"Add as new product":["Add as new product"],"Add as barcode to existing product":["Add as barcode to existing product"],"Add as new product and prefill barcode":["Add as new product and prefill barcode"],"Are you sure to delete quantity unit \"%s\"?":["Are you sure to delete quantity unit \"%s\"?"],"Are you sure to delete product \"%s\"?":["Are you sure to delete product \"%s\"?"],"Are you sure to delete location \"%s\"?":["Are you sure to delete location \"%s\"?"],"Are you sure to delete shopping location \"%s\"?":["Are you sure to delete shopping location \"%s\"?"],"Manage API keys":["Manage API keys"],"REST API & data model documentation":["REST API & data model documentation"],"API keys":["API keys"],"Create new API key":["Create new API key"],"API key":["API key"],"Expires":["Expires"],"Created":["Created"],"This product is not in stock":["This product is not in stock"],"This means %s will be added to stock":["This means %s will be added to stock"],"This means %s will be removed from stock":["This means %s will be removed from stock"],"This means it is estimated that a new execution of this chore is tracked %s days after the last was tracked":["This means it is estimated that a new execution of this chore is tracked %s days after the last was tracked"],"Removed %s %s of %s from stock":["Removed %s %s of %s from stock"],"About grocy":["About grocy"],"Close":["Close"],"%s batteries are due to be charged within the next %s days":["%s batteries are due to be charged within the next %s days"],"%s batteries are overdue to be charged":["%s batteries are overdue to be charged"],"%s chores are due to be done within the next %s days":["%s chores are due to be done within the next %s days"],"%s chores are overdue to be done":["%s chores are overdue to be done"],"Released on":["Released on"],"Consume %s %s of %s":["Consume %s %s of %s"],"Added %s %s of %s to stock":["Added %s %s of %s to stock"],"Stock amount of %s is now %s %s":["Stock amount of %s is now %s %s"],"Tracked execution of chore %s on %s":["Tracked execution of chore %s on %s"],"Tracked charge cycle of battery %s on %s":["Tracked charge cycle of battery %s on %s"],"Consume all %s which are currently in stock":["Consume all %s which are currently in stock"],"All":["All"],"Track charge cycle of battery %s":["Track charge cycle of battery %s"],"Track execution of chore %s":["Track execution of chore %s"],"Filter by location":["Filter by location"],"Search":["Search"],"Not logged in":["Not logged in"],"You have to select a product":["You have to select a product"],"You have to select a chore":["You have to select a chore"],"You have to select a battery":["You have to select a battery"],"A name is required":["A name is required"],"A location is required":["A location is required"],"The amount cannot be lower than %s":["The amount cannot be lower than %s"],"This cannot be negative":["This cannot be negative"],"A quantity unit is required":["A quantity unit is required"],"A period type is required":["A period type is required"],"A best before date is required":["A best before date is required"],"Settings":["Settings"],"This can only be before now":["This can only be before now"],"Calendar":["Calendar"],"Recipes":["Recipes"],"Edit recipe":["Edit recipe"],"New recipe":["New recipe"],"Ingredients list":["Ingredients list"],"Add recipe ingredient":["Add recipe ingredient"],"Edit recipe ingredient":["Edit recipe ingredient"],"Are you sure to delete recipe \"%s\"?":["Are you sure to delete recipe \"%s\"?"],"Are you sure to delete recipe ingredient \"%s\"?":["Are you sure to delete recipe ingredient \"%s\"?"],"Are you sure to empty shopping list \"%s\"?":["Are you sure to empty shopping list \"%s\"?"],"Clear list":["Clear list"],"Requirements fulfilled":["Requirements fulfilled"],"Put missing products on shopping list":["Put missing products on shopping list"],"Not enough in stock, %s ingredients missing":["Not enough in stock, %s ingredients missing"],"Enough in stock":["Enough in stock"],"Not enough in stock, %s ingredients missing but already on the shopping list":["Not enough in stock, %s ingredients missing but already on the shopping list"],"Expand to fullscreen":["Expand to fullscreen"],"Ingredients":["Ingredients"],"Preparation":["Preparation"],"Recipe":["Recipe"],"Not enough in stock, %s missing, %s already on shopping list":["Not enough in stock, %s missing, %s already on shopping list"],"Show notes":["Show notes"],"Put missing amount on shopping list":["Put missing amount on shopping list"],"Are you sure to put all missing ingredients for recipe \"%s\" on the shopping list?":["Are you sure to put all missing ingredients for recipe \"%s\" on the shopping list?"],"Added for recipe %s":["Added for recipe %s"],"Manage users":["Manage users"],"User":["User"],"Users":["Users"],"Are you sure to delete user \"%s\"?":["Are you sure to delete user \"%s\"?"],"Create user":["Create user"],"Edit user":["Edit user"],"First name":["First name"],"Last name":["Last name"],"A username is required":["A username is required"],"Confirm password":["Confirm password"],"Passwords do not match":["Passwords do not match"],"Change password":["Change password"],"Done by":["Done by"],"Last done by":["Last done by"],"Unknown":["Unknown"],"Filter by chore":["Filter by chore"],"Chores journal":["Chores journal"],"0 means suggestions for the next charge cycle are disabled":["0 means suggestions for the next charge cycle are disabled"],"Charge cycle interval (days)":["Charge cycle interval (days)"],"Last price":["Last price"],"Price history":["Price history"],"No price history available":["No price history available"],"Price":["Price"],"in %s per purchase quantity unit":["in %s per purchase quantity unit"],"The price cannot be lower than %s":["The price cannot be lower than %s"],"%s product expires within the next %s days":["%s product expires within the next %s days"],"%s product is already expired":["%s product is already expired"],"%s product is below defined min. stock amount":["%s product is below defined min. stock amount"],"Unit":["Unit"],"Units":["Units"],"%s chore is due to be done within the next %s days":["%s chore is due to be done within the next %s days"],"%s chore is overdue to be done":["%s chore is overdue to be done"],"%s battery is due to be charged within the next %s days":["%s battery is due to be charged within the next %s days"],"%s battery is overdue to be charged":["%s battery is overdue to be charged"],"%s unit was automatically added and will apply in addition to the amount entered here":["%s unit was automatically added and will apply in addition to the amount entered here"],"in singular form":["in singular form"],"in plural form":["in plural form"],"Never expires":["Never expires"],"This cannot be lower than %s":["This cannot be lower than %s"],"-1 means that this product never expires":["-1 means that this product never expires"],"Quantity unit":["Quantity unit"],"Only check if a single unit is in stock (a different quantity can then be used above)":["Only check if a single unit is in stock (a different quantity can then be used above)"],"Are you sure to consume all ingredients needed by recipe \"%s\" (ingredients marked with \"check only if a single unit is in stock\" will be ignored)?":["Are you sure to consume all ingredients needed by recipe \"%s\" (ingredients marked with \"check only if a single unit is in stock\" will be ignored)?"],"Removed all ingredients of recipe \"%s\" from stock":["Removed all ingredients of recipe \"%s\" from stock"],"Consume all ingredients needed by this recipe":["Consume all ingredients needed by this recipe"],"Click to show technical details":["Click to show technical details"],"Error while saving, probably this item already exists":["Error while saving, probably this item already exists"],"Error details":["Error details"],"Tasks":["Tasks"],"Show done tasks":["Show done tasks"],"Task":["Task"],"Due":["Due"],"Assigned to":["Assigned to"],"Mark task \"%s\" as completed":["Mark task \"%s\" as completed"],"Uncategorized":["Uncategorized"],"Task categories":["Task categories"],"Create task":["Create task"],"A due date is required":["A due date is required"],"Category":["Category"],"Edit task":["Edit task"],"Are you sure to delete task \"%s\"?":["Are you sure to delete task \"%s\"?"],"%s task is due to be done within the next %s days":["%s task is due to be done within the next %s days"],"%s tasks are due to be done within the next %s days":["%s tasks are due to be done within the next %s days"],"%s task is overdue to be done":["%s task is overdue to be done"],"%s tasks are overdue to be done":["%s tasks are overdue to be done"],"Edit task category":["Edit task category"],"Create task category":["Create task category"],"Product groups":["Product groups"],"Ungrouped":["Ungrouped"],"Create product group":["Create product group"],"Edit product group":["Edit product group"],"Product group":["Product group"],"Are you sure to delete product group \"%s\"?":["Are you sure to delete product group \"%s\"?"],"Stay logged in permanently":["Stay logged in permanently"],"When not set, you will get logged out at latest after 30 days":["When not set, you will get logged out at latest after 30 days"],"Filter by status":["Filter by status"],"Below min. stock amount":["Below min. stock amount"],"Expiring soon":["Expiring soon"],"Already expired":["Already expired"],"Due soon":["Due soon"],"Overdue":["Overdue"],"View settings":["View settings"],"Auto reload on external changes":["Auto reload on external changes"],"Enable night mode":["Enable night mode"],"Auto enable in time range":["Auto enable in time range"],"From":["From"],"in format":["in format"],"To":["To"],"Time range goes over midnight":["Time range goes over midnight"],"Product picture":["Product picture"],"No file selected":["No file selected"],"If you don't select a file, the current picture will not be altered":["If you don't select a file, the current picture will not be altered"],"Delete":["Delete"],"The current picture will be deleted when you save the product":["The current picture will be deleted when you save the product"],"Select file":["Select file"],"Image of product %s":["Image of product %s"],"This product cannot be deleted because it is in stock, please remove the stock amount first.":["This product cannot be deleted because it is in stock, please remove the stock amount first."],"Delete not possible":["Delete not possible"],"Equipment":["Equipment"],"Instruction manual":["Instruction manual"],"The selected equipment has no instruction manual":["The selected equipment has no instruction manual"],"Notes":["Notes"],"Edit equipment":["Edit equipment"],"Create equipment":["Create equipment"],"If you don't select a file, the current instruction manual will not be altered":["If you don't select a file, the current instruction manual will not be altered"],"No instruction manual available":["No instruction manual available"],"The current instruction manual will be deleted when you save the equipment":["The current instruction manual will be deleted when you save the equipment"],"No picture available":["No picture available"],"Filter by product group":["Filter by product group"],"Presets for new products":["Presets for new products"],"Included recipes":["Included recipes"],"A recipe is required":["A recipe is required"],"Add included recipe":["Add included recipe"],"Edit included recipe":["Edit included recipe"],"Group":["Group"],"This will be used as a headline to group ingredients together":["This will be used as a headline to group ingredients together"],"Journal":["Journal"],"Stock journal":["Stock journal"],"Filter by product":["Filter by product"],"Booking time":["Booking time"],"Booking type":["Booking type"],"Undo booking":["Undo booking"],"Undone on":["Undone on"],"Batteries journal":["Batteries journal"],"Filter by battery":["Filter by battery"],"Undo charge cycle":["Undo charge cycle"],"Undo chore execution":["Undo chore execution"],"Chore execution successfully undone":["Chore execution successfully undone"],"Undo":["Undo"],"Booking successfully undone":["Booking successfully undone"],"Charge cycle successfully undone":["Charge cycle successfully undone"],"This cannot be negative and must be an integral number":["This cannot be negative and must be an integral number"],"Disable stock fulfillment checking for this ingredient":["Disable stock fulfillment checking for this ingredient"],"Add all list items to stock":["Add all list items to stock"],"Add %s %s of %s to stock":["Add %s %s of %s to stock"],"Adding shopping list item %s of %s":["Adding shopping list item %s of %s"],"Use a specific stock item":["Use a specific stock item"],"The first item in this list would be picked by the default rule which is \"First expiring first, then first in first out\"":["The first item in this list would be picked by the default rule which is \"First expiring first, then first in first out\""],"Mark %s %s of %s as open":["Mark %s %s of %s as open"],"When a product was marked as opened, the best before date will be replaced by today + this amount of days (a value of 0 disables this)":["When a product was marked as opened, the best before date will be replaced by today + this amount of days (a value of 0 disables this)"],"Default best before days after opened":["Default best before days after opened"],"Marked %s %s of %s as opened":["Marked %s %s of %s as opened"],"Mark as opened":["Mark as opened"],"Expires on %s; Bought on %s":["Expires on %s; Bought on %s"],"Not opened":["Not opened"],"Opened":["Opened"],"%s opened":["%s opened"],"Product expires":["Product expires"],"Task due":["Task due"],"Chore due":["Chore due"],"Battery charge cycle due":["Battery charge cycle due"],"Show clock in header":["Show clock in header"],"Stock settings":["Stock settings"],"Shopping list to stock workflow":["Shopping list to stock workflow"],"Automatically do the booking using the last price and the amount of the shopping list item, if the product has \"Default best before days\" set":["Automatically do the booking using the last price and the amount of the shopping list item, if the product has \"Default best before days\" set"],"Skip":["Skip"],"Servings":["Servings"],"Costs":["Costs"],"Based on the prices of the last purchase per product":["Based on the prices of the last purchase per product"],"The ingredients listed here result in this amount of servings":["The ingredients listed here result in this amount of servings"],"Do not check against the shopping list when adding missing items to it":["Do not check against the shopping list when adding missing items to it"],"By default the amount to be added to the shopping list is \"needed amount - stock amount - shopping list amount\" - when this is enabled, it is only checked against the stock amount, not against what is already on the shopping list":["By default the amount to be added to the shopping list is \"needed amount - stock amount - shopping list amount\" - when this is enabled, it is only checked against the stock amount, not against what is already on the shopping list"],"Picture":["Picture"],"Uncheck ingredients to not put them on the shopping list":["Uncheck ingredients to not put them on the shopping list"],"This is for statistical purposes only":["This is for statistical purposes only"],"You have to select a recipe":["You have to select a recipe"],"Key type":["Key type"],"Share\/Integrate calendar (iCal)":["Share\/Integrate calendar (iCal)"],"Use the following (public) URL to share or integrate the calendar in iCal format":["Use the following (public) URL to share or integrate the calendar in iCal format"],"Allow partial units in stock":["Allow partial units in stock"],"Enable tare weight handling":["Enable tare weight handling"],"This is useful e.g. for flour in jars - on purchase\/consume\/inventory you always weigh the whole jar, the amount to be posted is then automatically calculated based on what is in stock and the tare weight defined below":["This is useful e.g. for flour in jars - on purchase\/consume\/inventory you always weigh the whole jar, the amount to be posted is then automatically calculated based on what is in stock and the tare weight defined below"],"Tare weight":["Tare weight"],"Tare weight handling enabled - please weigh the whole container, the amount to be posted will be automatically calculcated":["Tare weight handling enabled - please weigh the whole container, the amount to be posted will be automatically calculcated"],"You have to select a location":["You have to select a location"],"You have to select a shopping location":["You have to select a shopping location"],"List":["List"],"Gallery":["Gallery"],"The current picture will be deleted when you save the recipe":["The current picture will be deleted when you save the recipe"],"Show product details":["Show product details"],"Stock journal for this product":["Stock journal for this product"],"Show chore details":["Show chore details"],"Journal for this chore":["Journal for this chore"],"Show battery details":["Show battery details"],"Journal for this battery":["Journal for this battery"],"System info":["System info"],"Changelog":["Changelog"],"will be multiplied a factor of %s to get %s":["will be multiplied a factor of %s to get %s"],"The given date is earlier than today, are you sure?":["The given date is earlier than today, are you sure?"],"Product count":["Product count"],"Type a new product name or barcode and hit TAB to start a workflow":["Type a new product name or barcode and hit TAB to start a workflow"],"This will be used as the default setting when adding this product as a recipe ingredient":["This will be used as the default setting when adding this product as a recipe ingredient"],"Add item":["Add item"],"Selected shopping list":["Selected shopping list"],"New shopping list":["New shopping list"],"Delete shopping list":["Delete shopping list"],"Chores settings":["Chores settings"],"Batteries settings":["Batteries settings"],"Tasks settings":["Tasks settings"],"Create shopping list":["Create shopping list"],"Are you sure to delete shopping list \"%s\"?":["Are you sure to delete shopping list \"%s\"?"],"Average shelf life":["Average shelf life"],"Spoil rate":["Spoil rate"],"Show more":["Show more"],"Show less":["Show less"],"The amount must be between %s and %s":["The amount must be between %s and %s"],"Day of month":["Day of month"],"Monday":["Monday"],"Tuesday":["Tuesday"],"Wednesday":["Wednesday"],"Thursday":["Thursday"],"Friday":["Friday"],"Saturday":["Saturday"],"Sunday":["Sunday"],"Configure userfields":["Configure userfields"],"Userfields":["Userfields"],"Filter by entity":["Filter by entity"],"Entity":["Entity"],"Caption":["Caption"],"Type":["Type"],"Create userfield":["Create userfield"],"A entity is required":["A entity is required"],"A caption is required":["A caption is required"],"A type is required":["A type is required"],"Show as column in tables":["Show as column in tables"],"This is required and can only contain letters and numbers":["This is required and can only contain letters and numbers"],"Edit userfield":["Edit userfield"],"no-assignment":["No assignment"],"who-least-did-first":["Who least did first"],"random":["Random"],"in-alphabetical-order":["In alphabetical order"],"timeago_locale":["en"],"timeago_nan":["NaN years ago"],"moment_locale":["x"],"datatables_localization":["{\"sEmptyTable\":\"No data available in table\",\"sInfo\":\"Showing _START_ to _END_ of _TOTAL_ entries\",\"sInfoEmpty\":\"Showing 0 to 0 of 0 entries\",\"sInfoFiltered\":\"(filtered from _MAX_ total entries)\",\"sInfoPostFix\":\"\",\"sInfoThousands\":\",\",\"sLengthMenu\":\"Show _MENU_ entries\",\"sLoadingRecords\":\"Loading...\",\"sProcessing\":\"Processing...\",\"sSearch\":\"Search:\",\"sZeroRecords\":\"No matching records found\",\"oPaginate\":{\"sFirst\":\"First\",\"sLast\":\"Last\",\"sNext\":\"Next\",\"sPrevious\":\"Previous\"},\"oAria\":{\"sSortAscending\":\": activate to sort column ascending\",\"sSortDescending\":\": activate to sort column descending\"}}"],"summernote_locale":["x"],"fullcalendar_locale":["x"],"bootstrap-select_locale":["x"],"purchase":["Purchase"],"transfer_to":["Transfer To"],"transfer_from":["Transfer From"],"consume":["Consume"],"inventory-correction":["Inventory correction"],"product-opened":["Product opened"],"stock-edit-old":["Stock entry edited (old values)"],"stock-edit-new":["Stock entry edited (new values)"],"self-production":["Self-production"],"manually":["Manually"],"daily":["Daily"],"weekly":["Weekly"],"monthly":["Monthly"],"yearly":["Yearly"],"hourly":["Hourly"],"adaptive":["Adaptive"],"text-single-line":["Text (single line)"],"text-multi-line":["Text (multi line)"],"number-integral":["Number (integral)"],"number-decimal":["Number (decimal)"],"date":["Date (without time)"],"datetime":["Date & time"],"checkbox":["Checkbox"],"preset-list":["Select list (a single item can be selected)"],"preset-checklist":["Select list (multiple items can be selected)"],"link":["Link"],"link-with-title":["Link (with title)"],"file":["File"],"image":["Image"],"ADMIN":["All permissions"],"USERS_CREATE":["Create users"],"USERS_EDIT":["Edit users (including passwords)"],"USERS_READ":["Show users"],"USERS_EDIT_SELF":["Edit own user data \/ change own password"],"BATTERIES_UNDO_CHARGE_CYCLE":["Undo charge cycle"],"BATTERIES_TRACK_CHARGE_CYCLE":["Track charge cycle"],"CHORE_TRACK_EXECUTION":["Track execution"],"CHORE_UNDO_EXECUTION":["Undo execution"],"MASTER_DATA_EDIT":["Edit master data"],"TASKS_UNDO_EXECUTION":["Undo execution"],"TASKS_MARK_COMPLETED":["Mark completed"],"STOCK_EDIT":["Edit stock entries"],"STOCK_TRANSFER":["Transfer"],"STOCK_INVENTORY":["Inventory"],"STOCK_CONSUME":["Consume"],"STOCK_OPEN":["Open products"],"STOCK_PURCHASE":["Purchase"],"SHOPPINGLIST_ITEMS_ADD":["Add items"],"SHOPPINGLIST_ITEMS_DELETE":["Remove items"],"USERS":["User management"],"STOCK":["Stock"],"SHOPPINGLIST":["Shopping list"],"CHORES":["Chores"],"BATTERIES":["Batteries"],"TASKS":["Tasks"],"RECIPES":["Recipes"],"EQUIPMENT":["Equipment"],"CALENDAR":["Calendar"],"RECIPES_MEALPLAN":["Meal plan"],"cs":["Czech"],"da":["Danish"],"de":["German"],"el_GR":["Greek"],"en":["English"],"en_GB":["English (Great Britain)"],"es":["Spanish"],"fr":["French"],"hu":["Hungarian"],"it":["Italian"],"ja":["Japanese"],"ko_KR":["Korean"],"nl":["Dutch"],"no":["Norwegian"],"pl":["Polish"],"pt_BR":["Portuguese (Brazil)"],"pt_PT":["Portuguese (Portugal)"],"ru":["Russian"],"sk_SK":["Slovak"],"sl":["Slovenian"],"sv_SE":["Swedish"],"tr":["Turkish"],"zh_TW":["Chinese (Taiwan)"],"zh_CN":["Chinese (China)"],"he_IL":["Hebrew (Israel)"],"ta":["Tamil"],"fi":["Finnish"],"ca":["Catalan"]}}};
                Grocy.LocalizationStringsQu = {"domain":null,"plural-forms":"","messages":{"":{"Piece":["Piece"],"Pack":["Pack"]}}};
                Grocy.FeatureFlags = {"GROCY_FEATURE_FLAG_STOCK":true,"GROCY_FEATURE_FLAG_SHOPPINGLIST":true,"GROCY_FEATURE_FLAG_RECIPES":true,"GROCY_FEATURE_FLAG_CHORES":true,"GROCY_FEATURE_FLAG_TASKS":true,"GROCY_FEATURE_FLAG_BATTERIES":true,"GROCY_FEATURE_FLAG_EQUIPMENT":true,"GROCY_FEATURE_FLAG_CALENDAR":true,"GROCY_FEATURE_FLAG_LABEL_PRINTER":false,"GROCY_FEATURE_FLAG_STOCK_PRICE_TRACKING":true,"GROCY_FEATURE_FLAG_STOCK_LOCATION_TRACKING":true,"GROCY_FEATURE_FLAG_STOCK_BEST_BEFORE_DATE_TRACKING":true,"GROCY_FEATURE_FLAG_STOCK_PRODUCT_OPENED_TRACKING":true,"GROCY_FEATURE_FLAG_STOCK_PRODUCT_FREEZING":true,"GROCY_FEATURE_FLAG_STOCK_BEST_BEFORE_DATE_FIELD_NUMBER_PAD":true,"GROCY_FEATURE_FLAG_SHOPPINGLIST_MULTIPLE_LISTS":true,"GROCY_FEATURE_FLAG_RECIPES_MEALPLAN":true,"GROCY_FEATURE_FLAG_CHORES_ASSIGNMENTS":true,"GROCY_FEATURE_FLAG_THERMAL_PRINTER":false,"GROCY_FEATURE_FLAG_DISABLE_BROWSER_BARCODE_CAMERA_SCANNING":false,"GROCY_FEATURE_FLAG_AUTO_TORCH_ON_WITH_CAMERA":true};
                Grocy.Webhooks = {
                                };

                                Grocy.UserSettings = { };
                Grocy.UserId = -1;
                        </script>
</head>

<body class="fixed-nav  ">
                <nav id="mainNav"
                class="navbar navbar-expand-lg navbar-light fixed-top">
                <a class="navbar-brand py-0"
                        href="http://127.0.0.1:8080/">
                        <img src="http://127.0.0.1:8080/img/logo.svg?v=4.0.0"
                                width="114"
                                height="30">
                </a>
                <span id="clock-container"
                        class="text-muted font-italic d-none">
                        <i class="fa-regular fa-clock"></i>
                        <span id="clock-small"
                                class="d-inline d-sm-none"></span>
                        <span id="clock-big"
                                class="d-none d-sm-inline"></span>
                </span>

                        </nav>

        <div class=" pt-0">
                <div class="container-fluid ">
                        <div class="row mb-3">
                                <div id="page-content"
                                        class="col content-text">
                                        <div class="row">
        <div class="col-lg-4 offset-lg-4 col-md-6 offset-md-3 col-12">
                <h2 class="text-center">Login</h2>

                <hr class="my-2">

                <form method="post"
                        action="http://127.0.0.1:8080/login"
                        id="login-form"
                        novalidate>

                        <div class="form-group">
                                <label for="name">Username</label>
                                <input type="text"
                                        class="form-control"
                                        required
                                        id="username"
                                        name="username">
                        </div>

                        <div class="form-group">
                                <label for="name">Password</label>
                                <input type="password"
                                        class="form-control"
                                        required
                                        id="password"
                                        name="password">
                                <div id="login-error"
                                        class="form-text text-danger d-none"></div>
                        </div>

                        <div class="form-group mt-n2">
                                <div class="custom-control custom-checkbox">
                                        <input type="checkbox"
                                                class="form-check-input custom-control-input"
                                                id="stay_logged_in"
                                                name="stay_logged_in">
                                        <label class="form-check-label custom-control-label"
                                                for="stay_logged_in">
                                                Stay logged in permanently
                                                <i class="fa-solid fa-question-circle text-muted"
                                                        data-toggle="tooltip"
                                                        data-trigger="hover click"
                                                        title="When not set, you will get logged out at latest after 30 days"></i>
                                        </label>
                                </div>
                        </div>

                        <button id="login-button"
                                class="btn btn-success">OK</button>

                </form>
        </div>
</div>
                                </div>
                        </div>
                </div>
        </div>

        <script src="http://127.0.0.1:8080/packages/jquery/dist/jquery.min.js?v=4.0.0"></script>
        <script src="http://127.0.0.1:8080/packages/bootstrap/dist/js/bootstrap.bundle.min.js?v=4.0.0"></script>
        <script src="http://127.0.0.1:8080/packages/bootbox/dist/bootbox.min.js?v=4.0.0"></script>
        <script src="http://127.0.0.1:8080/packages/jquery-serializejson/jquery.serializejson.min.js?v=4.0.0"></script>
        <script src="http://127.0.0.1:8080/packages/moment/min/moment.min.js?v=4.0.0"></script>
                <script src="http://127.0.0.1:8080/packages/toastr/build/toastr.min.js?v=4.0.0"></script>
        <script src="http://127.0.0.1:8080/packages/sprintf-js/dist/sprintf.min.js?v=4.0.0"></script>
        <script src="http://127.0.0.1:8080/packages/gettext-translator/dist/translator.js?v=4.0.0"></script>
        <script src="http://127.0.0.1:8080/packages/nosleep.js/dist/NoSleep.min.js?v=4.0.0"></script>

        <script src="http://127.0.0.1:8080/js/extensions.js?v=4.0.0"></script>
        <script src="http://127.0.0.1:8080/js/grocy_menu_layout.js?v=4.0.0"></script>
        <script src="http://127.0.0.1:8080/js/grocy.js?v=4.0.0"></script>
        <script src="http://127.0.0.1:8080/js/grocy_dbchangedhandling.js?v=4.0.0"></script>
        <script src="http://127.0.0.1:8080/js/grocy_wakelockhandling.js?v=4.0.0"></script>
        <script src="http://127.0.0.1:8080/js/grocy_nightmode.js?v=4.0.0"></script>
        <script src="http://127.0.0.1:8080/js/grocy_clock.js?v=4.0.0"></script>

                        <script src="http://127.0.0.1:8080/viewjs/login.js?v=4.0.0"></script>

        </body>

</html>
ms32035 commented 1 year ago

yours looks ok. I also tried from a device that never accessed it before, and restarted traeffik load balancer on the server to rule out caching issues. Still the same

jayaddison commented 1 year ago

I'm not 100% certain whether this would affect anything, but: could you try adding a cachebreaking querystring parameter when you access the login page?

For example: HTTP GET https://grocy.mydomain.com/login?_cachebreak=js8n1z7aj1

Despite the traefik restart, my best guess at the moment is that caching somewhere is indeed the problem.

If not, then my next guess would be some kind of component that is rewriting the HTML server-side before it is provided to the client.

ms32035 commented 1 year ago

_cachebreak didn't help

my full docker-compose

version: '3'

services:

  frontend:
    image: "grocy/frontend:v4.0.0"
    depends_on:
      - backend
    expose:
      - 8080
    read_only: true
    tmpfs:
      - /tmp
    restart: always
    environment:
      NODE_PATH: /var/www/public/packages
    networks:
      - grocy
      - grocy_backend
    labels:
      traefik.enable: true
      traefik.http.routers.grocy.rule: Host(`grocy.domain.com`)
      traefik.http.routers.grocy.entrypoints: websecure
      traefik.http.routers.grocy.tls: true
      traefik.docker.network: grocy
      traefik.http.routers.grocy.tls.certresolver: letsencrypt

  backend:
    image: "grocy/backend:v4.0.0"
    read_only: true
    tmpfs:
      - /tmp
    volumes:
      - app-db:/var/www/data
    env_file:
      - grocy.env
    restart: always
    networks:
      - grocy_backend

volumes:
  app-db:

networks:
  grocy:
    name: grocy
  grocy_backend:
    name: grocy_backend

if the rewrite is happening, its more like somewhere in the nginx inside the container

jayaddison commented 1 year ago

That all looks like a straightforward, sensible configuration to me. Puzzling, I can't think why the node_modules path component would continue to appear.

Client-side caching should have been ruled out by the cachebreaker.. for the sake of completeness, though, is there another client/browser you could try that could not have any previously-cached Grocy content?

ms32035 commented 1 year ago

I have already tried it

jayaddison commented 1 year ago

Ok. It seems unlikely to me that nginx would perform any rewriting here.. so.. I'm out of ideas. I'll leave this issue thread open; perhaps others may help, and/or I'll add any other ideas I think of later to help track this down.

ms32035 commented 1 year ago

I'm out of ideas too, unless it's something in the db?

maybe you can release a version with a symlink inside the container as a workaround?

jayaddison commented 1 year ago

Does adding a symlink inside the container resolve the problem for you?

ms32035 commented 1 year ago

It improves the situation but not entirely, as some files are still missing in the image:

/var/www/public/img $ ls -la
total 192
drwxr-xr-x    2 nginx    nginx         4096 Jul 29 19:32 .
drwxr-xr-x    1 nginx    nginx         4096 Jul 30 15:46 ..
-rw-r--r--    1 nginx    nginx       175680 Jul 29 19:32 icon-1024.png
-rw-r--r--    1 nginx    nginx         1948 Jul 29 19:32 icon-32.png
-rw-r--r--    1 nginx    nginx         1056 Jul 29 19:32 icon.svg
-rw-r--r--    1 nginx    nginx         2655 Jul 29 19:32 logo.svg
/var/www/public/node_modules $ ls -la
total 480
drwxr-xr-x  111 nginx    nginx         4096 Jul 29 19:32 .
drwxr-xr-x    1 nginx    nginx         4096 Jul 30 15:46 ..
drwxr-xr-x    2 nginx    nginx         4096 Jul 29 19:32 .bin
-rw-r--r--    1 nginx    nginx        20356 Jul 29 19:32 .yarn-integrity
drwxr-xr-x    3 nginx    nginx         4096 Jul 29 19:32 @babel
drwxr-xr-x    3 nginx    nginx         4096 Jul 29 19:32 @danielfarrell
drwxr-xr-x    3 nginx    nginx         4096 Jul 29 19:32 @ericblade
drwxr-xr-x    3 nginx    nginx         4096 Jul 29 19:32 @fontsource
drwxr-xr-x    3 nginx    nginx         4096 Jul 29 19:32 @fortawesome
drwxr-xr-x    4 nginx    nginx         4096 Jul 29 19:32 @types
drwxr-xr-x    5 nginx    nginx         4096 Jul 29 19:32 ajv
drwxr-xr-x    3 nginx    nginx         4096 Jul 29 19:32 animate.css
drwxr-xr-x    3 nginx    nginx         4096 Jul 29 19:32 asn1
drwxr-xr-x    2 nginx    nginx         4096 Jul 29 19:32 assert-plus
drwxr-xr-x    3 nginx    nginx         4096 Jul 29 19:32 asynckit
drwxr-xr-x    2 nginx    nginx         4096 Jul 29 19:32 aws-sign2
drwxr-xr-x    3 nginx    nginx         4096 Jul 29 19:32 aws4
drwxr-xr-x    2 nginx    nginx         4096 Jul 29 19:32 bcrypt-pbkdf
drwxr-xr-x    3 nginx    nginx         4096 Jul 29 19:32 bootbox
drwxr-xr-x    5 nginx    nginx         4096 Jul 29 19:32 bootstrap
drwxr-xr-x   10 nginx    nginx         4096 Jul 29 19:32 bootstrap-select
drwxr-xr-x    8 nginx    nginx         4096 Jul 29 19:32 bwip-js
drwxr-xr-x    2 nginx    nginx         4096 Jul 29 19:32 caseless
drwxr-xr-x    3 nginx    nginx         4096 Jul 29 19:32 chart.js
drwxr-xr-x    2 nginx    nginx         4096 Jul 29 19:32 chartjs-color
drwxr-xr-x    4 nginx    nginx         4096 Jul 29 19:32 chartjs-color-string
drwxr-xr-x    4 nginx    nginx         4096 Jul 29 19:32 chartjs-plugin-colorschemes
drwxr-xr-x    5 nginx    nginx         4096 Jul 29 19:32 chartjs-plugin-doughnutlabel
drwxr-xr-x    6 nginx    nginx         4096 Jul 29 19:32 chartjs-plugin-piechart-outlabels
drwxr-xr-x    2 nginx    nginx         4096 Jul 29 19:32 color-convert
drwxr-xr-x    2 nginx    nginx         4096 Jul 29 19:32 color-name
drwxr-xr-x    3 nginx    nginx         4096 Jul 29 19:32 combined-stream
drwxr-xr-x   13 nginx    nginx         4096 Jul 29 19:32 core-js
drwxr-xr-x    3 nginx    nginx         4096 Jul 29 19:32 core-util-is
drwxr-xr-x    3 nginx    nginx         4096 Jul 29 19:32 cwise-compiler
drwxr-xr-x    4 nginx    nginx         4096 Jul 29 19:32 dashdash
drwxr-xr-x    3 nginx    nginx         4096 Jul 29 19:32 data-uri-to-buffer
drwxr-xr-x    4 nginx    nginx         4096 Jul 29 19:32 datatables.net
drwxr-xr-x    6 nginx    nginx         4096 Jul 29 19:32 datatables.net-bs4
drwxr-xr-x    4 nginx    nginx         4096 Jul 29 19:32 datatables.net-colreorder
drwxr-xr-x    5 nginx    nginx         4096 Jul 29 19:32 datatables.net-colreorder-bs4
drwxr-xr-x   13 nginx    nginx         4096 Jul 29 19:32 datatables.net-plugins
drwxr-xr-x    4 nginx    nginx         4096 Jul 29 19:32 datatables.net-rowgroup
drwxr-xr-x    5 nginx    nginx         4096 Jul 29 19:32 datatables.net-rowgroup-bs4
drwxr-xr-x    4 nginx    nginx         4096 Jul 29 19:32 datatables.net-select
drwxr-xr-x    5 nginx    nginx         4096 Jul 29 19:32 datatables.net-select-bs4
drwxr-xr-x    4 nginx    nginx         4096 Jul 29 19:32 daterangepicker
drwxr-xr-x    3 nginx    nginx         4096 Jul 29 19:32 delayed-stream
drwxr-xr-x    3 nginx    nginx         4096 Jul 29 19:32 ecc-jsbn
drwxr-xr-x    2 nginx    nginx         4096 Jul 29 19:32 extend
drwxr-xr-x    3 nginx    nginx         4096 Jul 29 19:32 extsprintf
drwxr-xr-x    3 nginx    nginx         4096 Jul 29 19:32 fast-deep-equal
drwxr-xr-x    6 nginx    nginx         4096 Jul 29 19:32 fast-json-stable-stringify
drwxr-xr-x    2 nginx    nginx         4096 Jul 29 19:32 forever-agent
drwxr-xr-x    3 nginx    nginx         4096 Jul 29 19:32 form-data
drwxr-xr-x    3 nginx    nginx         4096 Jul 29 19:32 fullcalendar
drwxr-xr-x    2 nginx    nginx         4096 Jul 29 19:32 get-pixels
drwxr-xr-x    3 nginx    nginx         4096 Jul 29 19:32 getpass
drwxr-xr-x    4 nginx    nginx         4096 Jul 29 19:32 gettext-translator
drwxr-xr-x   14 nginx    nginx         4096 Jul 29 19:32 gl-matrix
drwxr-xr-x    3 nginx    nginx         4096 Jul 29 19:32 har-schema
drwxr-xr-x    3 nginx    nginx         4096 Jul 29 19:32 har-validator
drwxr-xr-x    4 nginx    nginx         4096 Jul 29 19:32 http-signature
drwxr-xr-x    2 nginx    nginx         4096 Jul 29 19:32 iota-array
drwxr-xr-x    3 nginx    nginx         4096 Jul 29 19:32 is-buffer
drwxr-xr-x    2 nginx    nginx         4096 Jul 29 19:32 is-typedarray
drwxr-xr-x    2 nginx    nginx         4096 Jul 29 19:32 isstream
drwxr-xr-x    4 nginx    nginx         4096 Jul 29 19:32 jpeg-js
drwxr-xr-x    4 nginx    nginx         4096 Jul 29 19:32 jquery
drwxr-xr-x    3 nginx    nginx         4096 Jul 29 19:32 jquery-serializejson
drwxr-xr-x    2 nginx    nginx         4096 Jul 29 19:32 jsbn
drwxr-xr-x    3 nginx    nginx         4096 Jul 29 19:32 json-schema
drwxr-xr-x    3 nginx    nginx         4096 Jul 29 19:32 json-schema-traverse
drwxr-xr-x    3 nginx    nginx         4096 Jul 29 19:32 json-stringify-safe
drwxr-xr-x    3 nginx    nginx         4096 Jul 29 19:32 jsprim
drwxr-xr-x    3 nginx    nginx        20480 Jul 29 19:32 lodash
drwxr-xr-x    2 nginx    nginx         4096 Jul 29 19:32 mime-db
drwxr-xr-x    2 nginx    nginx         4096 Jul 29 19:32 mime-types
drwxr-xr-x    7 nginx    nginx         4096 Jul 29 19:32 moment
drwxr-xr-x    4 nginx    nginx         4096 Jul 29 19:32 moment-timezone
drwxr-xr-x    2 nginx    nginx         4096 Jul 29 19:32 ndarray
drwxr-xr-x    2 nginx    nginx         4096 Jul 29 19:32 ndarray-linear-interpolate
drwxr-xr-x    2 nginx    nginx         4096 Jul 29 19:32 ndarray-pack
drwxr-xr-x    3 nginx    nginx         4096 Jul 29 19:32 node-bitmap
drwxr-xr-x    6 nginx    nginx         4096 Jul 29 19:32 nosleep.js
drwxr-xr-x    2 nginx    nginx         4096 Jul 29 19:32 oauth-sign
drwxr-xr-x    2 nginx    nginx         4096 Jul 29 19:32 omggif
drwxr-xr-x    3 nginx    nginx         4096 Jul 29 19:32 parse-data-uri
drwxr-xr-x    5 nginx    nginx         4096 Jul 29 19:32 performance-now
drwxr-xr-x    3 nginx    nginx         4096 Jul 29 19:32 pngjs
drwxr-xr-x    4 nginx    nginx         4096 Jul 29 19:32 popper.js
drwxr-xr-x    4 nginx    nginx         4096 Jul 29 19:32 psl
drwxr-xr-x    2 nginx    nginx         4096 Jul 29 19:32 punycode
drwxr-xr-x    6 nginx    nginx         4096 Jul 29 19:32 qs
drwxr-xr-x    2 nginx    nginx         4096 Jul 29 19:32 regenerator-runtime
drwxr-xr-x    4 nginx    nginx         4096 Jul 29 19:32 request
drwxr-xr-x    2 nginx    nginx         4096 Jul 29 19:32 safe-buffer
drwxr-xr-x    2 nginx    nginx         4096 Jul 29 19:32 safer-buffer
drwxr-xr-x    4 nginx    nginx         4096 Jul 29 19:32 sprintf-js
drwxr-xr-x    5 nginx    nginx         4096 Jul 29 19:32 sshpk
drwxr-xr-x   10 nginx    nginx         4096 Jul 29 19:32 summernote
drwxr-xr-x    2 nginx    nginx         4096 Jul 29 19:32 swagger-ui-dist
drwxr-xr-x    4 nginx    nginx         4096 Jul 29 19:32 tempusdominus-bootstrap-4
drwxr-xr-x    3 nginx    nginx         4096 Jul 29 19:32 through
drwxr-xr-x    5 nginx    nginx         4096 Jul 29 19:32 toastr
drwxr-xr-x    3 nginx    nginx         4096 Jul 29 19:32 tough-cookie
drwxr-xr-x    2 nginx    nginx         4096 Jul 29 19:32 tunnel-agent
drwxr-xr-x    2 nginx    nginx         4096 Jul 29 19:32 tweetnacl
drwxr-xr-x    3 nginx    nginx         4096 Jul 29 19:32 uniq
drwxr-xr-x    3 nginx    nginx         4096 Jul 29 19:32 uri-js
drwxr-xr-x    4 nginx    nginx         4096 Jul 29 19:32 uuid
drwxr-xr-x    4 nginx    nginx         4096 Jul 29 19:32 verror
ms32035 commented 1 year ago

@jayaddison any ideas on the missing modules? The only one I have is that the image you are building is different from the one on docker hub

jayaddison commented 1 year ago

Nope, I'm not sure what the problem is here @ms32035 - sorry. I don't think we should add symlinks or make other changes until we have identified the cause.

ms32035 commented 1 year ago

Maybe something with GH actions. The images from the hub are way smaller than if I build from the compose file:

grocy/frontend                              v4.0.0      2194240bea06   3 days ago     112MB
grocy/backend                               v4.0.0      ffe2898247c7   3 days ago     93.4MB

vs

grocy/frontend                                    v4.0.0               ceb95ad668a5   5 minutes ago   191MB
grocy/backend                                     v4.0.0               6ffd807021c2   6 minutes ago   113MB

the local one starts and looks good

jayaddison commented 1 year ago

What are the significant differences in the locally-built containers compared to the ones published on Docker Hub? Container images are basically compressed filesystem layers, and there should be a way to unpack those images.

ms32035 commented 1 year ago

ok, found the issue. there's a folder viewcache in the app-db volume. To make things trickier, that's mounted only on the backend container. After clearing it, all works fine now. Up to you how you want to address it.

jayaddison commented 1 year ago

Ah, perfect - thanks @ms32035, that makes sense. I hadn't tested a stateful upgrade from 3.x to 4.0.0 - but it is safe to remove the viewcache when updating. It would be nice to automate that in the containers.

ms32035 commented 1 year ago

Does it need to be persisted at all? Maybe use tmpfs for that folder

jayaddison commented 1 year ago

Good question; perhaps not. With an associated size limit, that seems like a potentially good solution to me.

berrnd commented 1 year ago

Does it need to be persisted at all?

data/viewcache holds "compiled" views/routes and other stuff expensive to generate on every request (theoretically, practically maybe not that noticeable on normal-fast machines). The definition only changes between releases, so it's only required to clear that on an update (doing that more often would not break anything, it's maybe just a performance thing as just mentioned).

jayaddison commented 1 year ago

@berrnd would you consider accepting a pull request to make view caching configurable (enable / disable)?

For the containers, I'm wondering whether it would be architecturally simplest not to store a viewcache at all; performance is likely not the utmost priority for containerised deployments, I'd guess..

ms32035 commented 1 year ago

Fully with you on that @berrnd . From a practical point of view, when running in docker, most of the cases when you restart the container is either an upgrade after pull, or machine restart. So tmpfs while not ideal, is quite a good solution

berrnd commented 1 year ago

would you consider accepting a pull request to make view caching configurable (enable / disable)?

Rather not.

 

performance is likely not the utmost priority for containerised deployments

Don't know if it's true for grocy-docker / if it also runs on ARM - but there were a lot requests in the past about performance complains when running Grocy on a Raspberry Pi. Naturally not a beast machine and that stuff is slow there would be not a suprise for me, but people seem to expect that things run fast even when deploying it on the controller of your microwave.

 

most of the cases when you restart the container is either an upgrade after pull, or machine restart. So tmpfs while not ideal, is quite a good solution

Sounds also for me that tmpfs is then just fine.

jayaddison commented 1 year ago

Ok, great - thank you both :+1: tmpfs sounds like the route to follow here.

jayaddison commented 1 year ago

This seems like a good issue to attempt to resolve during a container update for the recently-released Grocy 4.0.2 version.

jayaddison commented 1 year ago

Note: it seems that Docker tmpfs mounts (that are in use in these containers for the /tmp directory already) are only supported on Linux at the moment:

This functionality is only available if you’re running Docker on Linux.

That's probably OK, but good to be aware of.

jayaddison commented 1 year ago

The next question that I'm puzzling over is: how do we ensure that the filesystem mountpoints are resolved by Docker in the intended order.

We want the Docker volume named app-db to be mounted first, at /var/www/data in the backend container filesystem -- and then only after that do we want a tmpfs filesystem to be mounted at /var/www/data/viewcache, presenting a fresh, empty directory structure at that path to the system.

jayaddison commented 1 year ago

From what I've read on the moby/moby issue tracker, I think that volume mounts are resolved before tmpfs mounts are added; that's good, because that matches the desired behaviour here.

Some things to ensure in practice:

Note: there isn't a size limit on /tmp currently, but perhaps there should be, for consistency. It's probably worth checking what uses it, if anything, while the containers are running.

jayaddison commented 1 year ago

@ms32035 I'm testing a fix for this with pull request #220 - if you have any time to inspect that, it'd be appreciated. The change is small, but it still requires testing.

@berrnd I set an upper-bound of 4MB on the size of the viewcache tmpfs here, based on the size of the contents I saw after populating a fresh cache. Let me know whether that seems reasonable to you.

ms32035 commented 1 year ago

Works fine for me at least for now. The only question is the 4MB limit, but I'll leave it with you if you want to keep it, increase it or remove the limit altogether.

berrnd commented 1 year ago

Let me know whether that seems reasonable to you.

Based on what's currently stored there this is more than enough, but I can't say what maybe will be stored there in the future, that folder is generally meant to serve a (Grocy release specific) file based cache. If technically possible, I would not introduce such a low limit, but as said, currently that would fit as well.

jayaddison commented 1 year ago

Ok. I've increased the viewcache tmpfs size limit to 16MB, hopefully ample room for expansion while also being a low proportion of most (even RPi-style) systems' RAM.

I'm planning to merge that soon followed by an update to version 4.0.2 of Grocy for the containers.

jayaddison commented 1 year ago

One consideration to be aware of: although #220 updates the docker-compose.yml file to use tmpfs for the viewcache -- and I see as recently as today on Reddit that people do use that file to run these containers -- the 'raw' container as published to Docker Hub can be (mis)used in creative ways by anyone, and that's somewhat by-design.

In other words: the changes here should help to encourage the idea that the viewcache directory can be mounted as an ephemeral storage area, but it doesn't (and can't, afaik) mandate that in the built container images.