diff --git a/.github/workflows/repo-maintenance.yml b/.github/workflows/repo-maintenance.yml index d590cf912..d1f7e4fdd 100644 --- a/.github/workflows/repo-maintenance.yml +++ b/.github/workflows/repo-maintenance.yml @@ -27,7 +27,7 @@ jobs: stale-issue-message: > This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you - for your contributions. + for your contributions. See our [contributing guidelines](https://github.com/gethomepage/homepage/blob/main/CONTRIBUTING.md#automatic-respoistory-maintenance) for more details. lock-threads: name: 'Lock Old Threads' runs-on: ubuntu-latest @@ -42,14 +42,17 @@ jobs: This issue has been automatically locked since there has not been any recent activity after it was closed. Please open a new discussion for related concerns. + See our [contributing guidelines](https://github.com/gethomepage/homepage/blob/main/CONTRIBUTING.md#automatic-respoistory-maintenance) for more details. pr-comment: > This pull request has been automatically locked since there has not been any recent activity after it was closed. Please open a new discussion for related concerns. + See our [contributing guidelines](https://github.com/gethomepage/homepage/blob/main/CONTRIBUTING.md#automatic-respoistory-maintenance) for more details. discussion-comment: > This discussion has been automatically locked since there has not been any recent activity after it was closed. Please open a new discussion for related concerns. + See our [contributing guidelines](https://github.com/gethomepage/homepage/blob/main/CONTRIBUTING.md#automatic-respoistory-maintenance) for more details. close-answered-discussions: name: 'Close Answered Discussions' runs-on: ubuntu-latest @@ -89,7 +92,7 @@ jobs: }`; const commentVariables = { discussion: discussion.id, - body: 'This discussion has been automatically closed because it was marked as answered.', + body: 'This discussion has been automatically closed because it was marked as answered. See our [contributing guidelines](https://github.com/gethomepage/homepage/blob/main/CONTRIBUTING.md#automatic-respoistory-maintenance) for more details.', } await github.graphql(addCommentMutation, commentVariables) @@ -179,7 +182,85 @@ jobs: }`; const commentVariables = { discussion: discussion.id, - body: 'This discussion has been automatically closed due to inactivity.', + body: 'This discussion has been automatically closed due to inactivity. See our [contributing guidelines](https://github.com/gethomepage/homepage/blob/main/CONTRIBUTING.md#automatic-respoistory-maintenance) for more details.', + } + await github.graphql(addCommentMutation, commentVariables); + + const closeDiscussionMutation = `mutation($discussion:ID!, $reason:DiscussionCloseReason!) { + closeDiscussion(input:{discussionId:$discussion, reason:$reason}) { + clientMutationId + } + }`; + const closeVariables = { + discussion: discussion.id, + reason: "OUTDATED", + } + await github.graphql(closeDiscussionMutation, closeVariables); + + await sleep(1000); + } + } + close-unsupported-feature-requests: + name: 'Close Unsupported Feature Requests' + runs-on: ubuntu-latest + steps: + - uses: actions/github-script@v7 + with: + script: | + function sleep(ms) { + return new Promise(resolve => setTimeout(resolve, ms)); + } + + const CUTOFF_1_DAYS = 180; + const CUTOFF_1_COUNT = 5; + const CUTOFF_2_DAYS = 365; + const CUTOFF_2_COUNT = 10; + + const cutoff1Date = new Date(); + cutoff1Date.setDate(cutoff1Date.getDate() - CUTOFF_1_DAYS); + const cutoff2Date = new Date(); + cutoff2Date.setDate(cutoff2Date.getDate() - CUTOFF_2_DAYS); + + const query = `query( + $owner:String!, + $name:String!, + $featureRequestsCategory:ID!, + ) { + repository(owner:$owner, name:$name){ + discussions( + categoryId:$featureRequestsCategory, + last:100, + states:[OPEN], + ) { + nodes { + id, + number, + updatedAt, + upvoteCount, + } + }, + } + }`; + const variables = { + owner: context.repo.owner, + name: context.repo.repo, + featureRequestsCategory: "DIC_kwDOH31rQM4CRErS" + } + const result = await github.graphql(query, variables); + + for (const discussion of result.repository.discussions.nodes) { + const discussionDate = new Date(discussion.updatedAt); + if ((discussionDate < cutoff1Date && discussion.upvoteCount < CUTOFF_1_COUNT) || + (discussionDate < cutoff2Date && discussion.upvoteCount < CUTOFF_2_COUNT)) { + console.log(`Closing discussion #${discussion.number} (${discussion.id}), last updated at ${discussion.updatedAt} with votes ${discussion.upvoteCount}`); + const addCommentMutation = `mutation($discussion:ID!, $body:String!) { + addDiscussionComment(input:{discussionId:$discussion, body:$body}) { + clientMutationId + } + }`; + const commentVariables = { + discussion: discussion.id, + body: 'This discussion has been automatically closed due to lack of community support. See our [contributing guidelines](https://github.com/gethomepage/homepage/blob/main/CONTRIBUTING.md#automatic-respoistory-maintenance) for more details.', } await github.graphql(addCommentMutation, commentVariables); diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 51ca1f83e..f2361c434 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -51,3 +51,18 @@ By contributing, you agree that your contributions will be licensed under its GN ## References This document was adapted from the open-source contribution guidelines for [Facebook's Draft](https://github.com/facebook/draft-js/blob/main/CONTRIBUTING.md) + +# Automatic Respoistory Maintenance + +The homepage team appreciates all effort and interest from the community in filing bug reports, creating feature requests, sharing ideas and helping other community members. That said, in an effort to keep the repository organized and managebale the project uses automatic handling of certain areas: + +- Issues that cannot be reproduced will be marked 'stale' after 7 days of inactivity and closed after 14 further days of inactivity. +- Issues, pull requests and discussions that are closed will be locked after 30 days of inactivity. +- Discussions with a marked answer will be automatically closed. +- Discussions in the 'General' or 'Support' categories will be closed after 180 days of inactivity. +- Feature requests that do not meet the following thresholds will be closed: 5 "up-votes" after 180 days of inactivity or 10 "up-votes" after 365 days. + +In all cases, threads can be re-opened by project maintainers and, of course, users can always create a new discussion for related concerns. +Finally, remember that all information remains searchable and 'closed' feature requests can still serve as inspiration for new features. + +Thank you all for your contributions. diff --git a/docs/widgets/services/customapi.md b/docs/widgets/services/customapi.md index d392f0a9c..7f26f80fb 100644 --- a/docs/widgets/services/customapi.md +++ b/docs/widgets/services/customapi.md @@ -16,6 +16,8 @@ widget: password: password # auth - optional method: GET # optional, e.g. POST headers: # optional, must be object, see below + requestBody: # optional, can be string or object, see below + display: # optional, default to block, see below mappings: - field: key # needs to be YAML string or object label: Field 1 @@ -43,6 +45,15 @@ widget: locale: nl # optional style: short # optional - defaults to "long". Allowed values: `["long", "short", "narrow"]`. numeric: auto # optional - defaults to "always". Allowed values `["always", "auto"]`. + - field: key + label: Field 6 + format: text + additionalField: # optional + field: + hourly: + time: other key + color: theme # optional - defaults to "". Allowed values: `["theme", "adaptive", "black", "white"]`. + format: date # optional ``` Supported formats for the values are `text`, `number`, `float`, `percent`, `bytes`, `bitrate`, `date` and `relativeDate`. @@ -93,7 +104,7 @@ mappings: ## Data Transformation -You can manipulate data with the following tools `remap`, `scale` and `suffix`, for example: +You can manipulate data with the following tools `remap`, `scale`, `prefix` and `suffix`, for example: ```yaml - field: key4 @@ -110,7 +121,42 @@ You can manipulate data with the following tools `remap`, `scale` and `suffix`, label: Power format: float scale: 0.001 # can be number or string e.g. 1/16 - suffix: kW + suffix: "kW" +- field: key6 + label: Price + format: float + prefix: "$" +``` + +## List View + +You can change the default block view to a list view by setting the `display` option to `list`. + +The list view can optionally display an additional field next to the primary field. + +`additionalField`: Similar to `field`, but only used in list view. Displays additional information for the mapping object on the right. + +`field`: Defined the same way as other custom api widget fields. + +`color`: Allowed options: `"theme", "adaptive", "black", "white"`. The option `adaptive` will apply a color using the value of the `additionalField`, green for positive numbers, red for negative numbers. + +```yaml +- field: key + label: Field + format: text + remap: + - value: 0 + to: None + - value: 1 + to: Connected + - any: true # will map all other values + to: Unknown + additionalField: + field: + hourly: + time: key + color: theme + format: date ``` ## Custom Headers @@ -121,3 +167,16 @@ Pass custom headers using the `headers` option, for example: headers: X-API-Token: token ``` + +## Custom Request Body + +Pass custom request body using the `requestBody` option in either a string or object format. Objects will automatically be converted to a JSON string. + +```yaml +requestBody: + foo: bar +# or +requestBody: "{\"foo\":\"bar\"}" +``` + +Both formats result in `{"foo":"bar"}` being sent as the request body. Don't forget to set your `Content-Type` headers! diff --git a/docs/widgets/services/moonraker.md b/docs/widgets/services/moonraker.md index 6de62ec64..2ee1a4e2a 100644 --- a/docs/widgets/services/moonraker.md +++ b/docs/widgets/services/moonraker.md @@ -12,3 +12,12 @@ widget: type: moonraker url: http://moonraker.host.or.ip:port ``` + +If your moonraker instance has an active authorization and your homepage ip isn't whitelisted you need to add your api key ([Authorization Documentation](https://moonraker.readthedocs.io/en/latest/web_api/#authorization)). + +```yaml +widget: + type: moonraker + url: http://moonraker.host.or.ip:port + key: api_keymoonraker +``` diff --git a/docs/widgets/services/planit.md b/docs/widgets/services/planit.md new file mode 100644 index 000000000..d1cebfaad --- /dev/null +++ b/docs/widgets/services/planit.md @@ -0,0 +1,15 @@ +--- +title: Plant-it +description: Plant-it Widget Configuration +--- + +Learn more about [Plantit](https://github.com/MDeLuise/plant-it). + +API key can be created from the REST API. + +```yaml +widget: + type: plantit + url: http://plant-it.host.or.ip:port # api port + key: plantit-api-key +``` diff --git a/docs/widgets/services/truenas.md b/docs/widgets/services/truenas.md index 6d747ef17..243504905 100644 --- a/docs/widgets/services/truenas.md +++ b/docs/widgets/services/truenas.md @@ -9,6 +9,8 @@ Allowed fields: `["load", "uptime", "alerts"]`. To create an API Key, follow [the official TrueNAS documentation](https://www.truenas.com/docs/scale/scaletutorials/toptoolbar/managingapikeys/). +A detailed pool listing is disabled by default, but can be enabled with the `enablePools` option. + ```yaml widget: type: truenas @@ -16,4 +18,5 @@ widget: username: user # not required if using api key password: pass # not required if using api key key: yourtruenasapikey # not required if using username / password + enablePools: true # optional, defaults to false ``` diff --git a/mkdocs.yml b/mkdocs.yml index 572d36b4c..e9d531ba0 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -103,6 +103,7 @@ nav: - widgets/services/photoprism.md - widgets/services/pialert.md - widgets/services/pihole.md + - widgets/services/plantit.md - widgets/services/plex-tautulli.md - widgets/services/plex.md - widgets/services/portainer.md diff --git a/public/locales/en/common.json b/public/locales/en/common.json index 5521fd0c3..cc6846a00 100644 --- a/public/locales/en/common.json +++ b/public/locales/en/common.json @@ -825,5 +825,11 @@ "netdata": { "warnings": "Warnings", "criticals": "Criticals" + }, + "plantit": { + "events": "Events", + "plants": "Plants", + "photos": "Photos", + "species": "Species" } } diff --git a/public/locales/it/common.json b/public/locales/it/common.json index 6f10baf7b..e61fff8b6 100644 --- a/public/locales/it/common.json +++ b/public/locales/it/common.json @@ -803,5 +803,11 @@ "netdata": { "warnings": "Warnings", "criticals": "Criticals" + }, + "plantit": { + "events": "Eventi", + "plants": "Piante", + "species": "Specie", + "images": "Immagini" } } diff --git a/src/components/filecontent.jsx b/src/components/filecontent.jsx deleted file mode 100644 index 1dd6266a2..000000000 --- a/src/components/filecontent.jsx +++ /dev/null @@ -1,10 +0,0 @@ -import useSWR from "swr"; - -export default function FileContent({ path, loadingValue, errorValue, emptyValue = "" }) { - const fetcher = (url) => fetch(url).then((res) => res.text()); - const { data, error, isLoading } = useSWR(`/api/config/${path}`, fetcher); - - if (error) return errorValue; - if (isLoading) return loadingValue; - return data || emptyValue; -} diff --git a/src/pages/index.jsx b/src/pages/index.jsx index 4bf1d9a27..39ac6cf29 100644 --- a/src/pages/index.jsx +++ b/src/pages/index.jsx @@ -11,7 +11,6 @@ import { serverSideTranslations } from "next-i18next/serverSideTranslations"; import { useRouter } from "next/router"; import Tab, { slugify } from "components/tab"; -import FileContent from "components/filecontent"; import ServicesGroup from "components/services/group"; import BookmarksGroup from "components/bookmarks/group"; import Widget from "components/widgets/widget"; @@ -391,17 +390,10 @@ function Home({ initialSettings }) { )} + + {/* eslint-disable-line @next/next/no-css-tags */} - -