Compare commits

...

14 Commits

Author SHA1 Message Date
Maksim Eltyshev
91eb43f472 chore: Update version 2026-03-19 11:13:08 +01:00
dependabot[bot]
f47c188331 build(deps): Bump socket.io-parser from 4.2.5 to 4.2.6 in /client (#1589)
Bumps [socket.io-parser](https://github.com/socketio/socket.io) from 4.2.5 to 4.2.6.
- [Release notes](https://github.com/socketio/socket.io/releases)
- [Changelog](https://github.com/socketio/socket.io/blob/main/CHANGELOG.md)
- [Commits](https://github.com/socketio/socket.io/compare/socket.io-parser@4.2.5...socket.io-parser@4.2.6)

---
updated-dependencies:
- dependency-name: socket.io-parser
  dependency-version: 4.2.6
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-03-19 10:29:12 +01:00
dependabot[bot]
38a45c66e3 build(deps): Bump socket.io-parser from 4.2.5 to 4.2.6 in /server (#1588)
Bumps [socket.io-parser](https://github.com/socketio/socket.io) from 4.2.5 to 4.2.6.
- [Release notes](https://github.com/socketio/socket.io/releases)
- [Changelog](https://github.com/socketio/socket.io/blob/main/CHANGELOG.md)
- [Commits](https://github.com/socketio/socket.io/compare/socket.io-parser@4.2.5...socket.io-parser@4.2.6)

---
updated-dependencies:
- dependency-name: socket.io-parser
  dependency-version: 4.2.6
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-03-19 10:29:00 +01:00
Maksim Eltyshev
c342d2edd7 feat: Add ability to display card ages 2026-03-18 09:42:20 +01:00
Fabian Reinold
95e479cd3a feat(helm): Add inline custom terms configuration, fix symlink detection (#1585) 2026-03-16 21:29:04 +01:00
Maksim Eltyshev
d9ae02899d chore: Update dependencies 2026-03-16 16:04:51 +01:00
Roberto Fernández Iglesias
5e6195b252 feat: Support running under subpath (#1451) 2026-03-12 22:11:11 +01:00
Maksim Eltyshev
61a3ff55cc fix(build): Write swagger.json only once 2026-03-12 15:24:41 +01:00
Maksim Eltyshev
af0cd79535 docs: Remove accidental dollar sign from Swagger route definitions
Closes #1578
2026-03-11 11:47:30 +01:00
Hannes
bb907d62e4 feat: Add ability to expose Swagger specification (#1577) 2026-03-11 11:39:42 +01:00
Br1an
7604a31a74 fix(oidc): Add configurable HTTP timeout for OIDC client (#1575) 2026-03-11 10:30:16 +01:00
Maksim Eltyshev
2685e5d5fc chore: Update version 2026-03-01 16:53:11 +01:00
Maksim Eltyshev
5ebe320396 chore: Update dependencies 2026-03-01 16:42:19 +01:00
Maksim Eltyshev
66ff3b65c6 fix(proxy): Prevent external access to outgoing proxy in host-network mode 2026-03-01 15:57:12 +01:00
92 changed files with 2500 additions and 2424 deletions

View File

@@ -11,7 +11,7 @@ server/test
server/.tmp
server/.venv
server/views/index.html
server/views/index.ejs
server/data/*
!server/data/.gitkeep

View File

@@ -34,7 +34,7 @@ jobs:
working-directory: ./client
- name: Build client
run: DISABLE_ESLINT_PLUGIN=true npm run build
run: INDEX_FORMAT=ejs DISABLE_ESLINT_PLUGIN=true npm run build
working-directory: ./client
- name: Include licenses into dist
@@ -45,7 +45,7 @@ jobs:
- name: Include built client into dist
run: |
mv ../../client/dist/* public
cp public/index.html views
mv public/index.ejs views
working-directory: ./server/dist
- name: Create release package

View File

@@ -24,13 +24,13 @@ jobs:
- name: Build client
run: |
DISABLE_ESLINT_PLUGIN=true npm run build
INDEX_FORMAT=ejs DISABLE_ESLINT_PLUGIN=true npm run build
mv dist build
working-directory: ./client
- name: Update Dockerfile to use prebuilt client
run: |
sed -i '/^FROM node:22 AS client/,/^ && DISABLE_ESLINT_PLUGIN=true npm run build$/c\
sed -i '/^FROM node:22 AS client/,/^ && INDEX_FORMAT=ejs DISABLE_ESLINT_PLUGIN=true npm run build$/c\
FROM node:22 AS client\n\
WORKDIR /app\n\
COPY client/build /app/dist' Dockerfile

View File

@@ -38,13 +38,13 @@ jobs:
- name: Build client
run: |
DISABLE_ESLINT_PLUGIN=true npm run build
INDEX_FORMAT=ejs DISABLE_ESLINT_PLUGIN=true npm run build
mv dist build
working-directory: ./client
- name: Update Dockerfile to use prebuilt client
run: |
sed -i '/^FROM node:22 AS client/,/^ && DISABLE_ESLINT_PLUGIN=true npm run build$/c\
sed -i '/^FROM node:22 AS client/,/^ && INDEX_FORMAT=ejs DISABLE_ESLINT_PLUGIN=true npm run build$/c\
FROM node:22 AS client\n\
WORKDIR /app\n\
COPY client/build /app/dist' Dockerfile

View File

@@ -46,7 +46,7 @@ jobs:
run: |
npm install
cd client
npm run build
INDEX_FORMAT=ejs npm run build
- name: Set up and start server for testing
env:

View File

@@ -22,7 +22,7 @@ COPY client .
RUN npm install npm --global \
&& npm install --omit=dev \
&& DISABLE_ESLINT_PLUGIN=true npm run build
&& INDEX_FORMAT=ejs DISABLE_ESLINT_PLUGIN=true npm run build
# Stage 3: Final image
FROM node:22-alpine
@@ -41,12 +41,12 @@ COPY --from=server --chown=node:node /app/node_modules node_modules
COPY --from=server --chown=node:node /app/dist .
COPY --from=client --chown=node:node /app/dist public
COPY --from=client --chown=node:node /app/dist/index.html views
RUN python3 -m venv .venv \
&& .venv/bin/pip3 install --upgrade pip \
&& .venv/bin/pip3 install -r requirements.txt --no-cache-dir \
&& mv .env.sample .env \
&& mv public/index.ejs views \
&& npm config set update-notifier false
VOLUME /app/data

View File

@@ -15,13 +15,13 @@ type: application
# This is the chart version. This version number should be incremented each time you make changes
# to the chart and its templates, including the app version.
# Versions are expected to follow Semantic Versioning (https://semver.org/)
version: 2.0.2
version: 2.1.0
# This is the version number of the application being deployed. This version number should be
# incremented each time you make changes to the application. Versions are not expected to
# follow Semantic Versioning. They should reflect the version the application is using.
# It is recommended to use it with quotes.
appVersion: "2.0.2"
appVersion: "2.1.0"
dependencies:
- alias: postgresql

View File

@@ -227,6 +227,28 @@ extraEnv:
key: api-key
```
### Custom Terms of Service
You can provide your own End User Terms of Service by passing the markdown files directly via `values.yaml` in the `terms` configuration block. This automates the creation of a corresponding ConfigMap and volume mount.
```yaml
terms:
enabled: true
customFiles:
en-US.md: |
# End User Terms of Service
...
[confirmations]::
---
✔️ **I have read and accept these End User Terms of Service**
de-DE.md: |
# Nutzungsbedingungen
...
[confirmations]::
---
✔️ **Ich habe diese Nutzungsbedingungen gelesen und akzeptiere sie**
```
### Image Digest Pinning
For enhanced security and reproducibility, you can pin the container image using its SHA256 digest instead of relying solely on tags. This ensures you always deploy the exact same image, preventing tag mutations or accidental updates.
@@ -291,3 +313,4 @@ image:
### Complete Example
See `values-example.yaml` for a comprehensive example that demonstrates all the advanced features including OIDC configuration with custom CA certificates.
````

View File

@@ -0,0 +1,13 @@
{{- if .Values.terms.enabled }}
apiVersion: v1
kind: ConfigMap
metadata:
name: {{ include "planka.fullname" . }}-terms
labels:
{{- include "planka.labels" . | nindent 4 }}
data:
{{- range $key, $value := .Values.terms.customFiles }}
{{ $key }}: |
{{- $value | nindent 4 }}
{{- end }}
{{- end }}

View File

@@ -77,6 +77,10 @@ spec:
subPath: tmp
name: emptydir
{{- end }}
{{- if .Values.terms.enabled }}
- mountPath: /app/terms/custom
name: planka-terms
{{- end }}
{{- /* Extra volume mounts */}}
{{- range .Values.extraMounts }}
- name: {{ .name }}
@@ -222,6 +226,11 @@ spec:
{{- if .Values.securityContext.readOnlyRootFilesystem }}
- name: emptydir
emptyDir: {}
{{- end }}
{{- if .Values.terms.enabled }}
- name: planka-terms
configMap:
name: {{ include "planka.fullname" . }}-terms
{{- end }}
{{- /* Extra volumes */}}
{{- range .Values.extraMounts }}

View File

@@ -248,6 +248,22 @@ extraEnv: []
## - name: SMTP_FROM
## value: "your_email@example.com"
## End User Terms of Service configuration
## Mount custom terms of service markdown files into the Planka deployment
##
terms:
enabled: false
# Provide individual language files as key-value pairs
# e.g.,
# customFiles:
# en-US.md: |
# # End User Terms of Service
# ...
# de-DE.md: |
# # Nutzungsbedingungen
# ...
customFiles: {}
## Extra volume mounts configuration
## Mount ConfigMaps, Secrets, and arbitrary volumes to the PLANKA container
## This allows mounting any pre-existing ConfigMaps, Secrets, or other volume types

2563
client/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -79,9 +79,6 @@
}
},
"overrides": {
"@diplodoc/transform": {
"lodash": "^4.17.23"
},
"react-mentions": {
"@babel/runtime": "^7.28.6"
}
@@ -89,12 +86,12 @@
"dependencies": {
"@ballerina/highlightjs-ballerina": "^1.0.1",
"@diplodoc/cut-extension": "^1.1.1",
"@diplodoc/transform": "^4.64.1",
"@diplodoc/transform": "^4.70.2",
"@gravity-ui/components": "^4.18.0",
"@gravity-ui/markdown-editor": "^15.31.0",
"@gravity-ui/uikit": "^7.31.1",
"@gravity-ui/markdown-editor": "^15.35.1",
"@gravity-ui/uikit": "^7.34.0",
"@juggle/resize-observer": "^3.4.0",
"@vitejs/plugin-react": "^5.1.3",
"@vitejs/plugin-react": "^5.2.0",
"browserslist-to-esbuild": "^2.1.1",
"classnames": "^2.5.1",
"date-fns": "^4.1.0",
@@ -123,10 +120,10 @@
"highlightjs-zenscript": "^2.0.0",
"hightlightjs-papyrus": "^0.0.4",
"history": "^5.3.0",
"i18next": "^25.8.1",
"i18next-browser-languagedetector": "^8.2.0",
"i18next": "^25.8.18",
"i18next-browser-languagedetector": "^8.2.1",
"initials": "^3.1.2",
"javascript-time-ago": "^2.6.2",
"javascript-time-ago": "^2.6.4",
"js-cookie": "^3.0.5",
"jwt-decode": "^4.0.0",
"linkify-react": "^4.3.2",
@@ -134,7 +131,7 @@
"lodash": "^4.17.23",
"lowlight": "^3.3.0",
"markdown-it": "^13.0.2",
"nanoid": "^5.1.6",
"nanoid": "^5.1.7",
"papaparse": "^5.5.3",
"patch-package": "^8.0.1",
"photoswipe": "^5.4.4",
@@ -143,25 +140,25 @@
"react-beautiful-dnd": "^13.1.1",
"react-datepicker": "^9.1.0",
"react-dom": "18.2.0",
"react-dropzone": "^14.4.0",
"react-dropzone": "^15.0.0",
"react-frame-component": "^5.2.7",
"react-hot-toast": "^2.6.0",
"react-i18next": "^16.5.4",
"react-i18next": "^16.5.8",
"react-input-mask": "^2.0.4",
"react-intersection-observer": "^10.0.2",
"react-intersection-observer": "^10.0.3",
"react-mentions": "^4.4.10",
"react-photoswipe-gallery": "^4.0.0",
"react-redux": "^9.2.0",
"react-router": "^7.13.0",
"react-router": "^7.13.1",
"react-textarea-autosize": "^8.5.9",
"react-time-ago": "^7.4.1",
"react-time-ago": "^7.4.4",
"redux": "^5.0.1",
"redux-logger": "^3.0.6",
"redux-orm": "^0.16.2",
"redux-saga": "^1.4.2",
"reselect": "^5.1.1",
"sails.io.js": "^1.2.1",
"sass-embedded": "^1.97.3",
"sass-embedded": "^1.98.0",
"semantic-ui-react": "^2.1.5",
"socket.io-client": "^4.8.3",
"validator": "^13.15.26",
@@ -174,10 +171,10 @@
"devDependencies": {
"@babel/eslint-parser": "^7.28.6",
"@babel/preset-env": "^7.29.0",
"@cucumber/cucumber": "^12.6.0",
"@cucumber/pretty-formatter": "^3.0.0",
"@playwright/test": "^1.58.1",
"babel-jest": "^30.2.0",
"@cucumber/cucumber": "^12.7.0",
"@cucumber/pretty-formatter": "^3.2.0",
"@playwright/test": "^1.58.2",
"babel-jest": "^30.3.0",
"babel-preset-airbnb": "^5.0.0",
"eslint": "^8.57.1",
"eslint-config-airbnb": "^19.0.4",
@@ -187,8 +184,8 @@
"eslint-plugin-prettier": "^5.5.5",
"eslint-plugin-react": "^7.37.5",
"eslint-plugin-react-hooks": "^4.6.2",
"jest": "^30.2.0",
"playwright": "^1.58.0",
"jest": "^30.3.0",
"playwright": "^1.58.2",
"prettier": "3.8.1"
}
}

View File

@@ -1,8 +1,8 @@
diff --git a/node_modules/@diplodoc/transform/lib/md.js b/node_modules/@diplodoc/transform/lib/md.js
index a2d222e..8d1377e 100644
index c9faa96..e4bef9b 100644
--- a/node_modules/@diplodoc/transform/lib/md.js
+++ b/node_modules/@diplodoc/transform/lib/md.js
@@ -101,8 +101,12 @@ function initPlugins(md, options, pluginOptions) {
@@ -107,8 +107,12 @@ function initPlugins(md, options, pluginOptions) {
}
md.use(ol_attr_conversion_1.olAttrConversion);
plugins.forEach((plugin) => md.use(plugin, pluginOptions));

View File

@@ -1,8 +1,8 @@
diff --git a/node_modules/@gravity-ui/markdown-editor/build/esm/bundle/wysiwyg-preset.js b/node_modules/@gravity-ui/markdown-editor/build/esm/bundle/wysiwyg-preset.js
index dec01f0..a80b857 100644
index c0d13c3..4c6e4e9 100644
--- a/node_modules/@gravity-ui/markdown-editor/build/esm/bundle/wysiwyg-preset.js
+++ b/node_modules/@gravity-ui/markdown-editor/build/esm/bundle/wysiwyg-preset.js
@@ -102,7 +102,6 @@ export const BundlePreset = (builder, opts) => {
@@ -107,7 +107,6 @@ export const BundlePreset = (builder, opts) => {
enableNewImageSizeCalculation: opts.enableNewImageSizeCalculation,
...opts.imgSize,
},
@@ -10,7 +10,7 @@ index dec01f0..a80b857 100644
deflist: {
deflistTermPlaceholder: () => i18nPlaceholder('deflist_term'),
deflistDescPlaceholder: () => i18nPlaceholder('deflist_desc'),
@@ -123,11 +122,6 @@ export const BundlePreset = (builder, opts) => {
@@ -128,11 +127,6 @@ export const BundlePreset = (builder, opts) => {
...opts.yfmTable,
controls: opts.mobile ? false : opts.yfmTable?.controls,
},
@@ -36,6 +36,23 @@ index 8aefe20..99e59e3 100644
}
if (options.pmTransformers) {
this.#pmTransformers = options.pmTransformers;
diff --git a/node_modules/@gravity-ui/markdown-editor/build/esm/extensions/markdown/CodeBlock/CodeBlockHighlight/TooltipPlugin/index.js b/node_modules/@gravity-ui/markdown-editor/build/esm/extensions/markdown/CodeBlock/CodeBlockHighlight/TooltipPlugin/index.js
index 5eec9bb..3abd31a 100644
--- a/node_modules/@gravity-ui/markdown-editor/build/esm/extensions/markdown/CodeBlock/CodeBlockHighlight/TooltipPlugin/index.js
+++ b/node_modules/@gravity-ui/markdown-editor/build/esm/extensions/markdown/CodeBlock/CodeBlockHighlight/TooltipPlugin/index.js
@@ -75,12 +75,6 @@ export const codeLangSelectTooltipViewCreator = (view, langItems, mapping = {},
dispatch: view.dispatch,
}),
},
- {
- id: 'code-block-copy',
- type: ToolbarDataType.ReactNodeFn,
- width: 28,
- content: () => _jsx(ClipboardButton, { text: node.textContent }),
- },
].filter(isTruthy),
[
{
diff --git a/node_modules/@gravity-ui/markdown-editor/build/esm/extensions/yfm/YfmNote/YfmNoteSpecs/index.js b/node_modules/@gravity-ui/markdown-editor/build/esm/extensions/yfm/YfmNote/YfmNoteSpecs/index.js
index 212c583..b709383 100644
--- a/node_modules/@gravity-ui/markdown-editor/build/esm/extensions/yfm/YfmNote/YfmNoteSpecs/index.js

View File

@@ -18,7 +18,7 @@ const http = {};
return result;
}, new FormData());
return fetch(`${Config.SERVER_BASE_URL}/api${url}`, {
return fetch(`${Config.BASE_PATH}/api${url}`, {
method,
headers,
body: formData,

View File

@@ -10,7 +10,7 @@ import Config from '../constants/Config';
const io = sailsIOClient(socketIOClient);
io.sails.url = Config.SERVER_BASE_URL;
io.sails.path = `${Config.BASE_PATH}/socket.io`;
io.sails.autoConnect = false;
io.sails.reconnection = true;
io.sails.useCORSRouteToGetCookie = false;

View File

@@ -1,4 +1,19 @@
# [2.0.2] - 2026-02-23
# [2.1.0] - 2026-03-19
### Added
* Support running under subpath
* Add ability to display card ages
* Allow exposing Swagger specification
* Configurable HTTP timeout for OIDC
## [2.0.3] - 2026-03-01
### Fixed
* Improve security by ensuring the outgoing proxy is not accessible from outside
## [2.0.2] - 2026-02-23
### Fixed

View File

@@ -43,6 +43,14 @@ const Others = React.memo(() => {
className={styles.radio}
onChange={handleChange}
/>
<Radio
toggle
name="displayCardAges"
checked={board.displayCardAges}
label={t('common.displayCardAges')}
className={styles.radio}
onChange={handleChange}
/>
<Radio
toggle
name="expandTaskListsByDefault"

View File

@@ -17,6 +17,7 @@ import { BoardMembershipRoles, BoardViews } from '../../../constants/Enums';
import TaskList from './TaskList';
import DueDateChip from '../DueDateChip';
import StopwatchChip from '../StopwatchChip';
import TimeAgo from '../../common/TimeAgo';
import UserAvatar from '../../users/UserAvatar';
import LabelChip from '../../labels/LabelChip';
import CustomFieldValueChip from '../../custom-field-values/CustomFieldValueChip';
@@ -75,12 +76,13 @@ const ProjectContent = React.memo(({ cardId }) => {
return attachment && attachment.data.thumbnailUrls.outside360;
});
const { listName, withCreator } = useSelector((state) => {
const { listName, withCreator, withAge } = useSelector((state) => {
const board = selectors.selectCurrentBoard(state);
return {
listName: list.name && (board.view === BoardViews.KANBAN ? null : list.name),
withCreator: board.alwaysDisplayCardCreator,
withAge: board.displayCardAges,
};
}, shallowEqual);
@@ -115,6 +117,7 @@ const ProjectContent = React.memo(({ cardId }) => {
card.dueDate ||
card.stopwatch ||
card.commentsTotal > 0 ||
withAge ||
attachmentsTotal > 0 ||
notificationsTotal > 0 ||
listName;
@@ -236,6 +239,14 @@ const ProjectContent = React.memo(({ cardId }) => {
</span>
</span>
)}
{withAge && card.createdAt && (
<span className={classNames(styles.attachment, styles.attachmentLeft)}>
<span className={styles.attachmentContent}>
<Icon name="history" />
<TimeAgo date={card.createdAt} />
</span>
</span>
)}
</span>
)}
{!isCompact && usersNode}

View File

@@ -6,12 +6,13 @@
import React, { useMemo } from 'react';
import PropTypes from 'prop-types';
import classNames from 'classnames';
import { useSelector } from 'react-redux';
import { shallowEqual, useSelector } from 'react-redux';
import { Icon } from 'semantic-ui-react';
import selectors from '../../../selectors';
import markdownToText from '../../../utils/markdown-to-text';
import { BoardViews } from '../../../constants/Enums';
import TimeAgo from '../../common/TimeAgo';
import LabelChip from '../../labels/LabelChip';
import CustomFieldValueChip from '../../custom-field-values/CustomFieldValueChip';
@@ -52,19 +53,14 @@ const StoryContent = React.memo(({ cardId }) => {
selectNotificationsTotalByCardId(state, cardId),
);
const listName = useSelector((state) => {
if (!list.name) {
return null;
}
const { listName, withAge } = useSelector((state) => {
const board = selectors.selectCurrentBoard(state);
const { view } = selectors.selectCurrentBoard(state);
if (view === BoardViews.KANBAN) {
return null;
}
return list.name;
});
return {
listName: list.name && (board.view === BoardViews.KANBAN ? null : list.name),
withAge: board.displayCardAges,
};
}, shallowEqual);
const coverUrl = useSelector((state) => {
const attachment = selectAttachmentById(state, card.coverAttachmentId);
@@ -109,7 +105,7 @@ const StoryContent = React.memo(({ cardId }) => {
{card.name}
</div>
{card.description && <div className={styles.descriptionText}>{descriptionText}</div>}
{(attachmentsTotal > 0 || notificationsTotal > 0 || listName) && (
{(withAge || attachmentsTotal > 0 || notificationsTotal > 0 || listName) && (
<span className={styles.attachments}>
{notificationsTotal > 0 && (
<span
@@ -138,6 +134,14 @@ const StoryContent = React.memo(({ cardId }) => {
</span>
</span>
)}
{withAge && card.createdAt && (
<span className={classNames(styles.attachment, styles.attachmentLeft)}>
<span className={styles.attachmentContent}>
<Icon name="history" />
<TimeAgo date={card.createdAt} />
</span>
</span>
)}
</span>
)}
</div>

View File

@@ -10,6 +10,7 @@ import { useSelector } from 'react-redux';
import history from '../../../history';
import selectors from '../../../selectors';
import matchPaths from '../../../utils/match-paths';
import Config from '../../../constants/Config';
import Paths from '../../../constants/Paths';
const Link = React.memo(({ href, content, stopPropagation, ...props }) => {
@@ -23,7 +24,8 @@ const Link = React.memo(({ href, content, stopPropagation, ...props }) => {
}
}, [href]);
const isSameSite = !!url && url.origin === window.location.origin;
const isSameSite =
!!url && url.origin === window.location.origin && url.pathname.startsWith(Config.BASE_PATH);
const cardsPathMatch = useMemo(() => {
if (!isSameSite) {

View File

@@ -4,6 +4,7 @@
*/
import history from '../../history';
import Config from '../../constants/Config';
const SAME_SITE_CLASS = 'same-site';
@@ -30,7 +31,9 @@ function process(token, nextToken) {
return;
}
const isSameSite = url.origin === window.location.origin;
const isSameSite =
url.origin === window.location.origin && url.pathname.startsWith(Config.BASE_PATH);
const trimOrigin = isSameSite && nextToken.type === 'text' && nextToken.content === href;
if (isSameSite) {

View File

@@ -3,8 +3,7 @@
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
*/
const SERVER_BASE_URL =
import.meta.env.VITE_SERVER_BASE_URL || (import.meta.env.DEV ? 'http://localhost:1337' : '');
const BASE_PATH = window.BASE_PATH || '';
const ACCESS_TOKEN_KEY = 'accessToken';
const ACCESS_TOKEN_VERSION_KEY = 'accessTokenVersion';
@@ -20,7 +19,7 @@ const MAX_SIZE_TO_DISPLAY_CONTENT = 256 * 1024;
const IS_MAC = navigator.platform.startsWith('Mac');
export default {
SERVER_BASE_URL,
BASE_PATH,
ACCESS_TOKEN_KEY,
ACCESS_TOKEN_VERSION_KEY,
ACCESS_TOKEN_VERSION,

View File

@@ -3,12 +3,14 @@
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
*/
const ROOT = '/';
const LOGIN = '/login';
const OIDC_CALLBACK = '/oidc-callback';
const PROJECTS = '/projects/:id';
const BOARDS = '/boards/:id';
const CARDS = '/cards/:id';
import Config from './Config';
const ROOT = `${Config.BASE_PATH}/`;
const LOGIN = `${Config.BASE_PATH}/login`;
const OIDC_CALLBACK = `${Config.BASE_PATH}/oidc-callback`;
const PROJECTS = `${Config.BASE_PATH}/projects/:id`;
const BOARDS = `${Config.BASE_PATH}/boards/:id`;
const CARDS = `${Config.BASE_PATH}/cards/:id`;
export default {
ROOT,

View File

@@ -166,6 +166,7 @@ export default {
deletedUser_title: 'مستخدم محذوف',
description: 'الوصف',
display: 'عرض',
displayCardAges: 'عرض أعمار البطاقات',
dropFileToUpload: 'أفلت الملف لرفعه',
dueDate_title: 'تاريخ الاستحقاق',
dynamicAndUnevenlySpacedLayout: 'تخطيط ديناميكي وغير متساوي المسافات.',

View File

@@ -178,6 +178,7 @@ export default {
deletedUser_title: 'Изтрит потребител',
description: 'Описание',
display: 'Показване',
displayCardAges: 'Показвай възрастта на картите',
dropFileToUpload: 'Пуснете файл за качване',
dueDate_title: 'Краен срок',
dynamicAndUnevenlySpacedLayout: 'Динамично и неравномерно разположение.',

View File

@@ -177,6 +177,7 @@ export default {
deletedUser_title: 'Usuari eliminat',
description: 'Descripció',
display: 'Mostrar',
displayCardAges: "Mostrar l'antiguitat de les targetes",
dropFileToUpload: 'Arrossega fitxer per pujar-lo',
dueDate_title: 'Data de venciment',
dynamicAndUnevenlySpacedLayout: 'Disseny dinàmic i amb espaiat irregular.',

View File

@@ -169,6 +169,7 @@ export default {
deletedUser_title: 'Smazaný uživatel',
description: 'Popis',
display: 'Zobrazit',
displayCardAges: 'Zobrazit stáří karet',
dropFileToUpload: 'Přetažením nahrát soubor',
dueDate_title: 'Termín',
dynamicAndUnevenlySpacedLayout: 'Dynamické a nerovnoměrné rozložení.',

View File

@@ -173,6 +173,7 @@ export default {
deletedUser_title: 'Slettet bruger',
description: 'Beskrivelse',
display: 'Vis',
displayCardAges: 'Vis kortalder',
dropFileToUpload: 'Slip fil for at uploade',
dueDate_title: 'Frist',
dynamicAndUnevenlySpacedLayout: 'Dynamisk og ujævnt fordelt layout.',

View File

@@ -188,6 +188,7 @@ export default {
deletedUser_title: 'Gelöschter Benutzer',
description: 'Beschreibung',
display: 'Anzeige',
displayCardAges: 'Kartenalter anzeigen',
dropFileToUpload: 'Datei für Upload ablegen',
dueDate_title: 'Fälligkeitsdatum',
dynamicAndUnevenlySpacedLayout: 'Dynamisches und ungleichmäßig verteiltes Layout.',

View File

@@ -186,6 +186,7 @@ export default {
deletedUser_title: 'Διαγραμμένος χρήστης',
description: 'Περιγραφή',
display: 'Εμφάνιση',
displayCardAges: 'Εμφάνιση ηλικίας καρτών',
dropFileToUpload: 'Σύρετε το αρχείο για μεταφόρτωση',
dueDate_title: 'Ημερομηνία λήξης',
dynamicAndUnevenlySpacedLayout: 'Δυναμική και άνισα κατανεμημένη διάταξη.',

View File

@@ -172,6 +172,7 @@ export default {
deletedUser_title: 'Deleted User',
description: 'Description',
display: 'Display',
displayCardAges: 'Display card ages',
dropFileToUpload: 'Drop file to upload',
dueDate_title: 'Due Date',
dynamicAndUnevenlySpacedLayout: 'Dynamic and unevenly spaced layout.',

View File

@@ -167,6 +167,7 @@ export default {
deletedUser_title: 'Deleted User',
description: 'Description',
display: 'Display',
displayCardAges: 'Display card ages',
dropFileToUpload: 'Drop file to upload',
dueDate_title: 'Due Date',
dynamicAndUnevenlySpacedLayout: 'Dynamic and unevenly spaced layout.',

View File

@@ -178,6 +178,7 @@ export default {
deletedUser_title: 'Usuario eliminado',
description: 'Descripción',
display: 'Mostrar',
displayCardAges: 'Mostrar antigüedad de las tarjetas',
dropFileToUpload: 'Arrastra archivo para subir',
dueDate_title: 'Fecha de vencimiento',
dynamicAndUnevenlySpacedLayout: 'Diseño dinámico y con espaciado irregular.',

View File

@@ -173,6 +173,7 @@ export default {
deletedUser_title: 'Kustutatud kasutaja',
description: 'Kirjeldus',
display: 'Kuva',
displayCardAges: 'Näita kaartide vanust',
dropFileToUpload: 'Lase faili üleslaadida',
dueDate_title: 'Tähtaeg',
dynamicAndUnevenlySpacedLayout: 'Dünaamiline ja eirastega kujundus.',

View File

@@ -176,6 +176,7 @@ export default {
deletedUser_title: 'کاربر حذف شده',
description: 'توضیحات',
display: 'نمایش',
displayCardAges: 'نمایش سن کارت‌ها',
dropFileToUpload: 'فایل را برای آپلود بکشید',
dueDate_title: 'تاریخ سررسید',
dynamicAndUnevenlySpacedLayout: 'طرح‌بندی پویا و نامتقارن.',

View File

@@ -169,6 +169,7 @@ export default {
deletedUser_title: 'Poistettu käyttäjä',
description: 'Kuvaus',
display: 'Näyttö',
displayCardAges: 'Näytä korttien ikä',
dropFileToUpload: 'Pudota tiedosto ladattavaksi',
dueDate_title: 'Määräpäivä',
dynamicAndUnevenlySpacedLayout: 'Dynaaminen ja epätasaisesti jaettu asettelu.',

View File

@@ -177,6 +177,7 @@ export default {
deletedUser_title: 'Utilisateur supprimé',
description: 'Description',
display: 'Affichage',
displayCardAges: "Afficher l'âge des cartes",
dropFileToUpload: 'Déposer le fichier à télécharger',
dueDate_title: "Date d'échéance",
dynamicAndUnevenlySpacedLayout: 'Mise en page dynamique et inégalement espacée.',

View File

@@ -167,6 +167,7 @@ export default {
deletedUser_title: 'Törölt felhasználó',
description: 'Leírás',
display: 'Megjelenítés',
displayCardAges: 'Kártyák korának megjelenítése',
dropFileToUpload: 'Dobja ide a fájlt a feltöltéshez',
dueDate_title: 'Esedékesség dátuma',
dynamicAndUnevenlySpacedLayout: 'Dinamikus és asszimetrikus kiosztás.',

View File

@@ -174,6 +174,7 @@ export default {
deletedUser_title: 'Pengguna yang dihapus',
description: 'Deskripsi',
display: 'Tampilan',
displayCardAges: 'Tampilkan usia kartu',
dropFileToUpload: 'Tarik berkas untuk menggungah',
dueDate_title: 'Tenggat waktu',
dynamicAndUnevenlySpacedLayout: 'Tata letak dinamis dan tidak merata.',

View File

@@ -174,6 +174,7 @@ export default {
deletedUser_title: 'Utente eliminato',
description: 'Descrizione',
display: 'Mostra',
displayCardAges: 'Mostra età delle schede',
dropFileToUpload: 'Trascina il file da caricare',
dueDate_title: 'Data di scadenza',
dynamicAndUnevenlySpacedLayout: 'Layout dinamico e irregolarmente distribuito.',

View File

@@ -170,6 +170,7 @@ export default {
deletedUser_title: '削除されたユーザー',
description: '説明',
display: '表示',
displayCardAges: 'カードの経過時間を表示',
dropFileToUpload: 'ファイルをドロップしてアップロード',
dueDate_title: '期限',
dynamicAndUnevenlySpacedLayout: '動的で不均等な間隔のレイアウト。',

View File

@@ -164,6 +164,7 @@ export default {
deletedUser_title: '삭제된 사용자',
description: '설명',
display: '표시',
displayCardAges: '카드 경과 시간 표시',
dropFileToUpload: '업로드할 파일을 드롭하세요',
dueDate_title: '마감일',
dynamicAndUnevenlySpacedLayout: '동적이고 불균등한 간격의 레이아웃.',

View File

@@ -173,6 +173,7 @@ export default {
deletedUser_title: 'Verwijderde gebruiker',
description: 'Beschrijving',
display: 'Weergave',
displayCardAges: 'Kaartleeftijd weergeven',
dropFileToUpload: 'Sleep bestand om te uploaden',
dueDate_title: 'Vervaldatum',
dynamicAndUnevenlySpacedLayout: 'Dynamische en ongelijk verdeelde indeling.',

View File

@@ -172,6 +172,7 @@ export default {
deletedUser_title: 'Usunięty użytkownik',
description: 'Opis',
display: 'Wyświetlanie',
displayCardAges: 'Pokazuj wiek kart',
dropFileToUpload: 'Upuść plik aby wgrać',
dueDate_title: 'Termin',
dynamicAndUnevenlySpacedLayout: 'Dynamiczny i nierówny układ.',

View File

@@ -175,6 +175,7 @@ export default {
deletedUser_title: 'Usuário excluído',
description: 'Descrição',
display: 'Exibir',
displayCardAges: 'Exibir idade dos cartões',
dropFileToUpload: 'Solte o arquivo para enviar',
dueDate_title: 'Data de vencimento',
dynamicAndUnevenlySpacedLayout: 'Layout dinâmico e desigualmente espaçado.',

View File

@@ -177,6 +177,7 @@ export default {
deletedUser_title: 'Utilizador eliminado',
description: 'Descrição',
display: 'Exibir',
displayCardAges: 'Mostrar idade dos cartões',
dropFileToUpload: 'Largue o ficheiro para carregar',
dueDate_title: 'Data de vencimento',
dynamicAndUnevenlySpacedLayout: 'Layout dinâmico e espaçamento irregular.',

View File

@@ -171,6 +171,7 @@ export default {
deletedUser_title: 'Utilizator șters',
description: 'Descriere',
display: 'Afișare',
displayCardAges: 'Afișează vârsta cardurilor',
dropFileToUpload: 'Aruncă fișierul pentru a încărca',
dueDate_title: 'Data scadentă',
dynamicAndUnevenlySpacedLayout: 'Aspect dinamic și spațiat neuniform.',

View File

@@ -174,6 +174,7 @@ export default {
deletedUser_title: 'Удалённый пользователь',
description: 'Описание',
display: 'Отображение',
displayCardAges: 'Отображать возраст карточек',
dropFileToUpload: 'Перетяните файл, чтобы загрузить',
dueDate_title: 'Срок исполнения',
dynamicAndUnevenlySpacedLayout: 'Динамичное и неравномерно распределённое расположение.',

View File

@@ -168,6 +168,7 @@ export default {
deletedUser_title: 'Zmazaný používateľ',
description: 'Popis',
display: 'Zobraziť',
displayCardAges: 'Zobraziť vek kariet',
dropFileToUpload: 'Potiahnutím nahraj súbor',
dueDate_title: 'Termín do',
dynamicAndUnevenlySpacedLayout: 'Dynamické a nerovnomerne rozložené usporiadanie.',

View File

@@ -171,6 +171,7 @@ export default {
deletedUser_title: 'Обрисан корисник',
description: 'Опис',
display: 'Приказ',
displayCardAges: 'Прикажи старост картица',
dropFileToUpload: 'Превуци датотеку за слање',
dueDate_title: 'Рок',
dynamicAndUnevenlySpacedLayout: 'Динамички и неравномерно распоређен изглед.',

View File

@@ -172,6 +172,7 @@ export default {
deletedUser_title: 'Obrisan korisnik',
description: 'Opis',
display: 'Prikaz',
displayCardAges: 'Prikaži starost kartica',
dropFileToUpload: 'Prevuci datoteku za slanje',
dueDate_title: 'Rok',
dynamicAndUnevenlySpacedLayout: 'Dinamički i neravnomerno raspoređen izgled.',

View File

@@ -178,6 +178,7 @@ export default {
deletedUser_title: 'Borttagen användare',
description: 'Beskrivning',
display: 'Visa',
displayCardAges: 'Visa kortålder',
dropFileToUpload: 'Släpp en fil för att ladda upp',
dueDate_title: 'Förfallodatum',
dynamicAndUnevenlySpacedLayout: 'Dynamisk och ojämnt fördelad layout.',

View File

@@ -174,6 +174,7 @@ export default {
deletedUser_title: 'Silinmiş kullanıcı',
description: 'açıklama',
display: 'Görüntüle',
displayCardAges: 'Kart yaşlarını göster',
dropFileToUpload: 'Yüklenecek dosyayı buraya bırakın',
dueDate_title: 'Termin tarihi',
dynamicAndUnevenlySpacedLayout: 'Dinamik ve düzensiz aralıklı düzen.',

View File

@@ -173,6 +173,7 @@ export default {
deletedUser_title: 'Видалений користувач',
description: 'Опис',
display: 'Дисплей',
displayCardAges: 'Відображати вік карток',
dropFileToUpload: 'Перетягніть файл для завантаження',
dueDate_title: 'Крайній термін',
dynamicAndUnevenlySpacedLayout: 'Динамічна та нерівномірна верстка.',

View File

@@ -169,6 +169,7 @@ export default {
deletedUser_title: "O'chirilgan foydalanuvchi",
description: 'Tavsif',
display: "Ko'rsatish",
displayCardAges: "Kartalar yoshini ko'rsatish",
dropFileToUpload: 'Faylni yuklash uchun qoldiring',
dueDate_title: 'Muddati',
dynamicAndUnevenlySpacedLayout: 'Dinamik va notekis joylashtirilgan tartib.',

View File

@@ -173,6 +173,7 @@ export default {
deletedUser_title: 'Người dùng đã bị xóa',
description: 'Mô tả',
display: 'Hiển thị',
displayCardAges: 'Hiển thị tuổi thẻ',
dropFileToUpload: 'Thả tệp vào đây để tải lên',
dueDate_title: 'Hạn chót',
dynamicAndUnevenlySpacedLayout: 'Bố cục động và khoảng cách không đều.',

View File

@@ -153,6 +153,7 @@ export default {
deletedUser_title: '已删除用户',
description: '描述',
display: '显示',
displayCardAges: '显示卡片创建时间',
dropFileToUpload: '拖放文件以上传',
dueDate_title: '截止日期',
dynamicAndUnevenlySpacedLayout: '动态非均匀间隔布局。',

View File

@@ -153,6 +153,7 @@ export default {
deletedUser_title: '已刪除的使用者',
description: '描述',
display: '顯示',
displayCardAges: '顯示卡片建立時間',
dropFileToUpload: '拖放文件以上傳',
dueDate_title: '截止日期',
dynamicAndUnevenlySpacedLayout: '動態不均勻間距佈局。',

View File

@@ -31,6 +31,7 @@ export default class extends BaseModel {
defaultCardType: attr(),
limitCardTypesToDefaultOne: attr(),
alwaysDisplayCardCreator: attr(),
displayCardAges: attr(),
expandTaskListsByDefault: attr(),
context: attr(),
view: attr(),

View File

@@ -8,24 +8,28 @@ import { jwtDecode } from 'jwt-decode';
import Config from '../constants/Config';
const PATH = Config.BASE_PATH || '/';
export const setAccessToken = (accessToken) => {
const { exp } = jwtDecode(accessToken);
const expires = new Date(exp * 1000);
Cookies.set(Config.ACCESS_TOKEN_KEY, accessToken, {
expires,
path: PATH,
secure: window.location.protocol === 'https:',
sameSite: 'strict',
});
Cookies.set(Config.ACCESS_TOKEN_VERSION_KEY, Config.ACCESS_TOKEN_VERSION, {
expires,
path: PATH,
});
};
export const removeAccessToken = () => {
Cookies.remove(Config.ACCESS_TOKEN_KEY);
Cookies.remove(Config.ACCESS_TOKEN_VERSION_KEY);
Cookies.remove(Config.ACCESS_TOKEN_KEY, { path: PATH });
Cookies.remove(Config.ACCESS_TOKEN_VERSION_KEY, { path: PATH });
};
export const getAccessToken = () => {

View File

@@ -1 +1 @@
export default '2.0.2';
export default '2.1.0';

View File

@@ -17,6 +17,6 @@ ln -s ${CLIENT_PATH}/logo512.png ${SERVER_PUBLIC_PATH}/logo512.png && echo "Link
ln -s ${CLIENT_PATH}/manifest.json ${SERVER_PUBLIC_PATH}/manifest.json && echo "Linked manifest.json successfully"
ln -s ${CLIENT_PATH}/robots.txt ${SERVER_PUBLIC_PATH}/robots.txt && echo "Linked robots.txt successfully"
ln -s ${CLIENT_PATH}/assets ${SERVER_PUBLIC_PATH}/assets && echo "Linked assets folder successfully"
ln -s ${CLIENT_PATH}/index.html ${SERVER_VIEWS_PATH}/index.html && echo "Linked index.html successfully"
ln -s ${CLIENT_PATH}/index.ejs ${SERVER_VIEWS_PATH}/index.ejs && echo "Linked index.ejs successfully"
echo "Setup symbolic links completed successfully."

View File

@@ -1,13 +1,42 @@
import fs from 'fs';
import path from 'path';
import { fileURLToPath } from 'url';
import { defineConfig } from 'vite';
import commonjs from 'vite-plugin-commonjs';
import { nodePolyfills } from 'vite-plugin-node-polyfills';
// eslint-disable-next-line import/no-unresolved
import react from '@vitejs/plugin-react';
import svgr from 'vite-plugin-svgr';
// eslint-disable-next-line import/no-unresolved
import browserslistToEsbuild from 'browserslist-to-esbuild';
// eslint-disable-next-line no-underscore-dangle
const __dirname = path.dirname(fileURLToPath(import.meta.url));
const createEjsTemplate = () => ({
name: 'create-ejs-template',
closeBundle() {
if (process.env.INDEX_FORMAT !== 'ejs') return;
const distPath = path.resolve(__dirname, 'dist');
const htmlPath = path.join(distPath, 'index.html');
if (!fs.existsSync(htmlPath)) return;
const html = fs.readFileSync(htmlPath, 'utf8');
const ejs = html
.replace(/(href|src)="\.\/([^"]+)"/g, '$1="<%- basePath %>/$2"')
.replace('</head>', " <script>window.BASE_PATH = '<%- basePath %>';</script>\n </head>");
fs.writeFileSync(path.join(distPath, 'index.ejs'), ejs);
fs.unlinkSync(htmlPath);
},
});
// https://vitejs.dev/config/
export default defineConfig({
base: './',
plugins: [
commonjs(),
nodePolyfills({
@@ -15,6 +44,7 @@ export default defineConfig({
}),
react(),
svgr(),
createEjsTemplate(),
],
resolve: {
alias: {
@@ -24,6 +54,10 @@ export default defineConfig({
server: {
port: 3000,
open: true,
proxy: {
'/api': 'http://localhost:1337',
'/socket.io': { target: 'http://localhost:1337', ws: true },
},
},
build: {
target: browserslistToEsbuild(['>0.2%', 'not dead', 'not op_mini all']),

View File

@@ -51,6 +51,9 @@ services:
# will be sent through this proxy if set.
# - OUTGOING_PROXY=http://proxy:3128
# Set to true to expose the Swagger specification at /swagger.json
# - SWAGGER_EXPOSED=false
# - S3_ENDPOINT=
# - S3_REGION=
# - S3_ACCESS_KEY_ID=
@@ -78,6 +81,7 @@ services:
# - OIDC_IGNORE_USERNAME=true
# - OIDC_IGNORE_ROLES=true
# - OIDC_ENFORCED=true
# - OIDC_TIMEOUT=3500
# - OIDC_DEBUG=true
# Email Notifications (https://nodemailer.com/smtp/)

View File

@@ -65,6 +65,9 @@ services:
# which you can control via OUTGOING_BLOCKED_* and OUTGOING_ALLOWED_* below.
# - OUTGOING_PROXY=http://proxy:3128
# Set to true to expose the Swagger specification at /swagger.json
# - SWAGGER_EXPOSED=false
# - S3_ENDPOINT=
# - S3_REGION=
# - S3_ACCESS_KEY_ID=
@@ -96,6 +99,7 @@ services:
# - OIDC_IGNORE_USERNAME=true
# - OIDC_IGNORE_ROLES=true
# - OIDC_ENFORCED=true
# - OIDC_TIMEOUT=3500
# - OIDC_DEBUG=true
# Email Notifications (https://nodemailer.com/smtp/)

192
package-lock.json generated
View File

@@ -1,24 +1,24 @@
{
"name": "planka",
"version": "2.0.2",
"version": "2.1.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "planka",
"version": "2.0.2",
"version": "2.1.0",
"hasInstallScript": true,
"dependencies": {
"concurrently": "^9.2.1",
"genversion": "^3.2.0",
"husky": "^9.1.7",
"lint-staged": "^16.2.7"
"lint-staged": "^16.4.0"
}
},
"node_modules/ansi-escapes": {
"version": "7.2.0",
"resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-7.2.0.tgz",
"integrity": "sha512-g6LhBsl+GBPRWGWsBtutpzBYuIIdBkLEvad5C/va/74Db018+5TZiyA26cZJAr3Rft5lprVqOIPxf5Vid6tqAw==",
"version": "7.3.0",
"resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-7.3.0.tgz",
"integrity": "sha512-BvU8nYgGQBxcmMuEeUEmNTvrMVjJNSH7RgW24vXexN4Ven6qCvy4TntnvlnwnMLTVlcRQQdbRY8NKnaIoeWDNg==",
"license": "MIT",
"dependencies": {
"environment": "^1.0.0"
@@ -78,18 +78,6 @@
"balanced-match": "^1.0.0"
}
},
"node_modules/braces": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz",
"integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==",
"license": "MIT",
"dependencies": {
"fill-range": "^7.1.1"
},
"engines": {
"node": ">=8"
}
},
"node_modules/chalk": {
"version": "4.1.2",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
@@ -134,13 +122,13 @@
}
},
"node_modules/cli-truncate": {
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-5.1.1.tgz",
"integrity": "sha512-SroPvNHxUnk+vIW/dOSfNqdy1sPEFkrTk6TUtqLCnBlo3N7TNYYkzzN7uSD6+jVjrdO4+p8nH7JzH6cIvUem6A==",
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-5.2.0.tgz",
"integrity": "sha512-xRwvIOMGrfOAnM1JYtqQImuaNtDEv9v6oIYAs4LIHwTiKee8uwvIi363igssOC0O5U04i4AlENs79LQLu9tEMw==",
"license": "MIT",
"dependencies": {
"slice-ansi": "^7.1.0",
"string-width": "^8.0.0"
"slice-ansi": "^8.0.0",
"string-width": "^8.2.0"
},
"engines": {
"node": ">=20"
@@ -330,26 +318,14 @@
"license": "MIT"
},
"node_modules/filelist": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.4.tgz",
"integrity": "sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q==",
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.6.tgz",
"integrity": "sha512-5giy2PkLYY1cP39p17Ech+2xlpTRL9HLspOfEgm0L6CwBXBTgsK5ou0JtzYuepxkaQ/tvhCFIJ5uXo0OrM2DxA==",
"license": "Apache-2.0",
"dependencies": {
"minimatch": "^5.0.1"
}
},
"node_modules/fill-range": {
"version": "7.1.1",
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz",
"integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==",
"license": "MIT",
"dependencies": {
"to-regex-range": "^5.0.1"
},
"engines": {
"node": ">=8"
}
},
"node_modules/find-package": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/find-package/-/find-package-1.0.0.tgz",
@@ -386,9 +362,9 @@
}
},
"node_modules/get-east-asian-width": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.4.0.tgz",
"integrity": "sha512-QZjmEOC+IT1uk6Rx0sX22V6uHWVwbdbxf1faPqJ1QhLdGgsRGCZoyaQBm/piRdJy/D2um6hM1UP7ZEeQ4EkP+Q==",
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.5.0.tgz",
"integrity": "sha512-CQ+bEO+Tva/qlmw24dCejulK5pMzVnUOFOijVogd3KQs07HnRIgp8TGipvCCRT06xeYEbpbgwaCxglFyiuIcmA==",
"license": "MIT",
"engines": {
"node": ">=18"
@@ -436,15 +412,6 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/is-number": {
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
"integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
"license": "MIT",
"engines": {
"node": ">=0.12.0"
}
},
"node_modules/jake": {
"version": "10.9.4",
"resolved": "https://registry.npmjs.org/jake/-/jake-10.9.4.tgz",
@@ -463,18 +430,17 @@
}
},
"node_modules/lint-staged": {
"version": "16.2.7",
"resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-16.2.7.tgz",
"integrity": "sha512-lDIj4RnYmK7/kXMya+qJsmkRFkGolciXjrsZ6PC25GdTfWOAWetR0ZbsNXRAj1EHHImRSalc+whZFg56F5DVow==",
"version": "16.4.0",
"resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-16.4.0.tgz",
"integrity": "sha512-lBWt8hujh/Cjysw5GYVmZpFHXDCgZzhrOm8vbcUdobADZNOK/bRshr2kM3DfgrrtR1DQhfupW9gnIXOfiFi+bw==",
"license": "MIT",
"dependencies": {
"commander": "^14.0.2",
"commander": "^14.0.3",
"listr2": "^9.0.5",
"micromatch": "^4.0.8",
"nano-spawn": "^2.0.0",
"pidtree": "^0.6.0",
"picomatch": "^4.0.3",
"string-argv": "^0.3.2",
"yaml": "^2.8.1"
"tinyexec": "^1.0.4",
"yaml": "^2.8.2"
},
"bin": {
"lint-staged": "bin/lint-staged.js"
@@ -531,17 +497,32 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/micromatch": {
"version": "4.0.8",
"resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz",
"integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==",
"node_modules/log-update/node_modules/ansi-styles": {
"version": "6.2.3",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz",
"integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==",
"license": "MIT",
"engines": {
"node": ">=12"
},
"funding": {
"url": "https://github.com/chalk/ansi-styles?sponsor=1"
}
},
"node_modules/log-update/node_modules/slice-ansi": {
"version": "7.1.2",
"resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-7.1.2.tgz",
"integrity": "sha512-iOBWFgUX7caIZiuutICxVgX1SdxwAVFFKwt1EvMYYec/NWO5meOJ6K5uQxhrYBdQJne4KxiqZc+KptFOWFSI9w==",
"license": "MIT",
"dependencies": {
"braces": "^3.0.3",
"picomatch": "^2.3.1"
"ansi-styles": "^6.2.1",
"is-fullwidth-code-point": "^5.0.0"
},
"engines": {
"node": ">=8.6"
"node": ">=18"
},
"funding": {
"url": "https://github.com/chalk/slice-ansi?sponsor=1"
}
},
"node_modules/mimic-function": {
@@ -557,9 +538,9 @@
}
},
"node_modules/minimatch": {
"version": "5.1.6",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz",
"integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==",
"version": "5.1.9",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.9.tgz",
"integrity": "sha512-7o1wEA2RyMP7Iu7GNba9vc0RWWGACJOCZBJX2GJWip0ikV+wcOsgVuY9uE8CPiyQhkGFSlhuSkZPavN7u1c2Fw==",
"license": "ISC",
"dependencies": {
"brace-expansion": "^2.0.1"
@@ -568,18 +549,6 @@
"node": ">=10"
}
},
"node_modules/nano-spawn": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/nano-spawn/-/nano-spawn-2.0.0.tgz",
"integrity": "sha512-tacvGzUY5o2D8CBh2rrwxyNojUsZNU2zjNTzKQrkgGJQTbGAfArVWXSKMBokBeeg6C7OLRGUEyoFlYbfeWQIqw==",
"license": "MIT",
"engines": {
"node": ">=20.17"
},
"funding": {
"url": "https://github.com/sindresorhus/nano-spawn?sponsor=1"
}
},
"node_modules/onetime": {
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/onetime/-/onetime-7.0.0.tgz",
@@ -620,29 +589,17 @@
"license": "ISC"
},
"node_modules/picomatch": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
"integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz",
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
"license": "MIT",
"engines": {
"node": ">=8.6"
"node": ">=12"
},
"funding": {
"url": "https://github.com/sponsors/jonschlinkert"
}
},
"node_modules/pidtree": {
"version": "0.6.0",
"resolved": "https://registry.npmjs.org/pidtree/-/pidtree-0.6.0.tgz",
"integrity": "sha512-eG2dWTVw5bzqGRztnHExczNxt5VGsE6OwTeCG3fdUf9KBsZzO3R5OIIIzWR+iZA0NtZ+RDVdaoE2dK1cn6jH4g==",
"license": "MIT",
"bin": {
"pidtree": "bin/pidtree.js"
},
"engines": {
"node": ">=0.10"
}
},
"node_modules/require-directory": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
@@ -708,16 +665,16 @@
}
},
"node_modules/slice-ansi": {
"version": "7.1.2",
"resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-7.1.2.tgz",
"integrity": "sha512-iOBWFgUX7caIZiuutICxVgX1SdxwAVFFKwt1EvMYYec/NWO5meOJ6K5uQxhrYBdQJne4KxiqZc+KptFOWFSI9w==",
"version": "8.0.0",
"resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-8.0.0.tgz",
"integrity": "sha512-stxByr12oeeOyY2BlviTNQlYV5xOj47GirPr4yA1hE9JCtxfQN0+tVbkxwCtYDQWhEKWFHsEK48ORg5jrouCAg==",
"license": "MIT",
"dependencies": {
"ansi-styles": "^6.2.1",
"is-fullwidth-code-point": "^5.0.0"
"ansi-styles": "^6.2.3",
"is-fullwidth-code-point": "^5.1.0"
},
"engines": {
"node": ">=18"
"node": ">=20"
},
"funding": {
"url": "https://github.com/chalk/slice-ansi?sponsor=1"
@@ -745,13 +702,13 @@
}
},
"node_modules/string-width": {
"version": "8.1.1",
"resolved": "https://registry.npmjs.org/string-width/-/string-width-8.1.1.tgz",
"integrity": "sha512-KpqHIdDL9KwYk22wEOg/VIqYbrnLeSApsKT/bSj6Ez7pn3CftUiLAv2Lccpq1ALcpLV9UX1Ppn92npZWu2w/aw==",
"version": "8.2.0",
"resolved": "https://registry.npmjs.org/string-width/-/string-width-8.2.0.tgz",
"integrity": "sha512-6hJPQ8N0V0P3SNmP6h2J99RLuzrWz2gvT7VnK5tKvrNqJoyS9W4/Fb8mo31UiPvy00z7DQXkP2hnKBVav76thw==",
"license": "MIT",
"dependencies": {
"get-east-asian-width": "^1.3.0",
"strip-ansi": "^7.1.0"
"get-east-asian-width": "^1.5.0",
"strip-ansi": "^7.1.2"
},
"engines": {
"node": ">=20"
@@ -761,12 +718,12 @@
}
},
"node_modules/strip-ansi": {
"version": "7.1.2",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.2.tgz",
"integrity": "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==",
"version": "7.2.0",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz",
"integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==",
"license": "MIT",
"dependencies": {
"ansi-regex": "^6.0.1"
"ansi-regex": "^6.2.2"
},
"engines": {
"node": ">=12"
@@ -790,16 +747,13 @@
"url": "https://github.com/chalk/supports-color?sponsor=1"
}
},
"node_modules/to-regex-range": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
"integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
"node_modules/tinyexec": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.0.4.tgz",
"integrity": "sha512-u9r3uZC0bdpGOXtlxUIdwf9pkmvhqJdrVCH9fapQtgy/OeTTMZ1nqH7agtvEfmGui6e1XxjcdrlxvxJvc3sMqw==",
"license": "MIT",
"dependencies": {
"is-number": "^7.0.0"
},
"engines": {
"node": ">=8.0"
"node": ">=18"
}
},
"node_modules/tree-kill": {

View File

@@ -1,6 +1,6 @@
{
"name": "planka",
"version": "2.0.2",
"version": "2.1.0",
"private": true,
"scripts": {
"client:build": "npm run build --prefix client",
@@ -39,6 +39,6 @@
"concurrently": "^9.2.1",
"genversion": "^3.2.0",
"husky": "^9.1.7",
"lint-staged": "^16.2.7"
"lint-staged": "^16.4.0"
}
}

View File

@@ -20,7 +20,7 @@ test
.tmp
.venv
views/index.html
views/index.ejs
data/*

View File

@@ -42,6 +42,9 @@ SECRET_KEY=notsecretkey
# will be sent through this proxy if set.
# OUTGOING_PROXY=http://proxy:3128
# Set to true to expose the Swagger specification at /swagger.json
# SWAGGER_EXPOSED=false
# S3_ENDPOINT=
# S3_REGION=
# S3_ACCESS_KEY_ID=
@@ -69,6 +72,7 @@ SECRET_KEY=notsecretkey
# OIDC_IGNORE_USERNAME=true
# OIDC_IGNORE_ROLES=true
# OIDC_ENFORCED=true
# OIDC_TIMEOUT=3500
# OIDC_DEBUG=true
# Email Notifications (https://nodemailer.com/smtp/)

2
server/.gitignore vendored
View File

@@ -134,7 +134,7 @@ swagger.json
dist
logs
views/index.html
views/index.ejs
data/*
!data/.gitkeep

View File

@@ -55,6 +55,10 @@
* type: boolean
* description: Whether to always display card creators
* example: false
* displayCardAges:
* type: boolean
* description: Whether to display card ages
* example: false
* expandTaskListsByDefault:
* type: boolean
* description: Whether to expand task lists by default
@@ -120,6 +124,9 @@ module.exports = {
alwaysDisplayCardCreator: {
type: 'boolean',
},
displayCardAges: {
type: 'boolean',
},
expandTaskListsByDefault: {
type: 'boolean',
},
@@ -160,6 +167,7 @@ module.exports = {
'defaultCardType',
'limitCardTypesToDefaultOne',
'alwaysDisplayCardCreator',
'displayCardAges',
'expandTaskListsByDefault',
);
}
@@ -178,6 +186,7 @@ module.exports = {
'defaultCardType',
'limitCardTypesToDefaultOne',
'alwaysDisplayCardCreator',
'displayCardAges',
'expandTaskListsByDefault',
'isSubscribed',
]);

View File

@@ -5,7 +5,7 @@
/**
* @swagger
* /cards/{cardId}/custom-field-values/customFieldGroupId:{customFieldGroupId}:customFieldId:${customFieldId}:
* /cards/{cardId}/custom-field-values/customFieldGroupId:{customFieldGroupId}:customFieldId:{customFieldId}:
* patch:
* summary: Create or update custom field value
* description: Creates or updates a custom field value for a card. Requires board editor permissions.

View File

@@ -5,7 +5,7 @@
/**
* @swagger
* /cards/{cardId}/custom-field-value/customFieldGroupId:{customFieldGroupId}:customFieldId:${customFieldId}:
* /cards/{cardId}/custom-field-value/customFieldGroupId:{customFieldGroupId}:customFieldId:{customFieldId}:
* delete:
* summary: Delete custom field value
* description: Deletes a custom field value for a specific card. Requires board editor permissions.

View File

@@ -0,0 +1,19 @@
/*!
* Copyright (c) 2025 PLANKA Software GmbH
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
*/
module.exports = {
exits: {
success: {
responseType: 'view',
viewTemplatePath: 'index',
},
},
fn() {
return {
basePath: sails.config.custom.baseUrlPath,
};
},
};

View File

@@ -0,0 +1,28 @@
/*!
* Copyright (c) 2024 PLANKA Software GmbH
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
*/
const fs = require('fs');
const path = require('path');
const SWAGGER_PATH = path.join(sails.config.appPath, 'swagger.json');
module.exports = {
async fn() {
if (!sails.config.custom.swaggerExposed) {
return this.res.notFound();
}
let specification;
try {
const content = fs.readFileSync(SWAGGER_PATH, 'utf8');
specification = JSON.parse(content);
} catch (error) {
sails.log.warn('swagger.json not found, run "npm run swagger:generate" to create it');
return this.res.notFound();
}
return specification;
},
};

View File

@@ -15,7 +15,7 @@ module.exports = {
fn(inputs) {
inputs.response.clearCookie('httpOnlyToken', {
path: sails.config.custom.baseUrlPath,
path: sails.config.custom.baseUrlPath || '/',
});
},
};

View File

@@ -24,7 +24,7 @@ module.exports = {
fn(inputs) {
inputs.response.cookie('httpOnlyToken', inputs.value, {
expires: new Date(inputs.accessTokenPayload.exp * 1000),
path: sails.config.custom.baseUrlPath,
path: sails.config.custom.baseUrlPath || '/',
secure: sails.config.custom.baseUrlSecure,
httpOnly: true,
sameSite: 'strict',

View File

@@ -45,6 +45,12 @@ module.exports = function defineOidcHook(sails) {
clientInitPromise = (async () => {
sails.log.info('Initializing OIDC client');
if (sails.config.custom.oidcTimeout !== null) {
openidClient.custom.setHttpOptionsDefaults({
timeout: sails.config.custom.oidcTimeout,
});
}
let issuer;
try {
issuer = await openidClient.Issuer.discover(sails.config.custom.oidcIssuer);

View File

@@ -32,7 +32,9 @@ module.exports = function defineTermsHook(sails) {
});
return entries
.filter((entry) => entry.isFile() && path.extname(entry.name) === '.md')
.filter(
(entry) => (entry.isFile() || entry.isSymbolicLink()) && path.extname(entry.name) === '.md',
)
.map((entry) => path.basename(entry.name, '.md'))
.sort();
};

View File

@@ -25,6 +25,7 @@
* - defaultCardType
* - limitCardTypesToDefaultOne
* - alwaysDisplayCardCreator
* - displayCardAges
* - expandTaskListsByDefault
* - createdAt
* - updatedAt
@@ -67,6 +68,11 @@
* default: false
* description: Whether to always display the card creator
* example: false
* displayCardAges:
* type: boolean
* default: false
* description: Whether to display card ages
* example: false
* expandTaskListsByDefault:
* type: boolean
* default: false
@@ -137,6 +143,11 @@ module.exports = {
defaultsTo: false,
columnName: 'always_display_card_creator',
},
displayCardAges: {
type: 'boolean',
defaultsTo: false,
columnName: 'display_card_ages',
},
expandTaskListsByDefault: {
type: 'boolean',
defaultsTo: false,

View File

@@ -2,6 +2,10 @@ const fs = require('fs');
const path = require('path');
// eslint-disable-next-line import/no-extraneous-dependencies
const ignore = require('ignore');
// eslint-disable-next-line import/no-extraneous-dependencies
const swaggerJsdoc = require('swagger-jsdoc');
const swaggerConfig = require('./config/swagger');
const OUT_DIR = 'dist';
@@ -40,3 +44,6 @@ const build = (src, dest) => {
};
build('./', OUT_DIR);
const specification = swaggerJsdoc(swaggerConfig);
fs.writeFileSync(path.join(OUT_DIR, 'swagger.json'), JSON.stringify(specification, null, 2));

View File

@@ -41,7 +41,7 @@ module.exports.custom = {
version,
baseUrl,
baseUrlPath: parsedBasedUrl.pathname,
baseUrlPath: parsedBasedUrl.pathname.replace(/\/$/, ''), // Remove trailing slash
baseUrlSecure: parsedBasedUrl.protocol === 'https:',
maxUploadFileSize: envToBytes(process.env.MAX_UPLOAD_FILE_SIZE),
@@ -64,6 +64,7 @@ module.exports.custom = {
showDetailedAuthErrors: process.env.SHOW_DETAILED_AUTH_ERRORS === 'true',
outgoingProxy: process.env.OUTGOING_PROXY,
swaggerExposed: process.env.SWAGGER_EXPOSED === 'true',
s3Endpoint: process.env.S3_ENDPOINT,
s3Region: process.env.S3_REGION,
@@ -92,6 +93,7 @@ module.exports.custom = {
oidcIgnoreUsername: process.env.OIDC_IGNORE_USERNAME === 'true',
oidcIgnoreRoles: process.env.OIDC_IGNORE_ROLES === 'true',
oidcEnforced: process.env.OIDC_ENFORCED === 'true',
oidcTimeout: envToNumber(process.env.OIDC_TIMEOUT),
oidcDebug: process.env.OIDC_DEBUG === 'true',
// TODO: move client base url to environment variable?

View File

@@ -44,6 +44,8 @@ module.exports.policies = {
'_internal/update-config': ['is-authenticated', 'is-internal'],
index: true,
'swagger/show': true,
'bootstrap/show': true,
'terms/show': true,
'access-tokens/create': true,

View File

@@ -235,6 +235,8 @@ module.exports.routes = {
'PATCH /api/_internal/config': '_internal/update-config',
'GET /swagger.json': 'swagger/show',
'GET /favicons/*': {
fn: protectedStaticDirServer('/favicons', () => sails.config.custom.faviconsPathSegment),
skipAssets: false,
@@ -264,7 +266,7 @@ module.exports.routes = {
},
'GET /*': {
view: 'index',
action: 'index',
skipAssets: true,
},
};

View File

@@ -24,7 +24,7 @@ module.exports.views = {
*
*/
extension: 'html',
// extension: 'ejs',
/**
*

View File

@@ -0,0 +1,19 @@
/*!
* Copyright (c) 2024 PLANKA Software GmbH
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
*/
module.exports.up = async (knex) => {
await knex.schema.alterTable('board', (table) => {
table.boolean('display_card_ages').notNullable().defaultTo(false);
});
return knex.schema.alterTable('board', (table) => {
table.boolean('display_card_ages').notNullable().alter();
});
};
module.exports.down = (knex) =>
knex.schema.alterTable('board', (table) => {
table.dropColumn('display_card_ages');
});

1663
server/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -49,8 +49,8 @@
}
},
"dependencies": {
"@aws-sdk/client-s3": "^3.995.0",
"@aws-sdk/lib-storage": "^3.995.0",
"@aws-sdk/client-s3": "^3.1009.0",
"@aws-sdk/lib-storage": "^3.1009.0",
"bcrypt": "^6.0.0",
"bytes": "^3.1.2",
"cross-env": "^10.1.0",
@@ -58,8 +58,8 @@
"dotenv-cli": "^11.0.0",
"escape-html": "^1.0.3",
"escape-markdown": "^1.0.4",
"file-type": "^21.3.0",
"fs-extra": "^11.3.3",
"file-type": "^21.3.2",
"fs-extra": "^11.3.4",
"i18n-2": "^0.7.3",
"ico-to-png": "^0.2.2",
"istextorbinary": "^9.5.0",
@@ -71,7 +71,7 @@
"nodemailer": "^7.0.13",
"openid-client": "^5.7.1",
"patch-package": "^8.0.1",
"pg": "^8.18.0",
"pg": "^8.20.0",
"read": "^5.0.1",
"rimraf": "^6.1.3",
"sails": "^1.5.17",
@@ -80,7 +80,7 @@
"sails-postgresql": "^5.0.1",
"serve-static": "^2.2.1",
"sharp": "^0.34.5",
"undici": "^7.22.0",
"undici": "^7.24.0",
"uuid": "^11.1.0",
"validator": "^13.15.26",
"winston": "^3.19.0",

View File

@@ -41,7 +41,7 @@ start_outgoing_proxy_if_needed() {
# Basic settings
echo "pid_filename $SQUID_PID" >> "$SQUID_CONF"
echo "http_port 3128" >> "$SQUID_CONF"
echo "http_port 127.0.0.1:3128" >> "$SQUID_CONF"
echo "acl all src all" >> "$SQUID_CONF"
# Disable caching
@@ -52,11 +52,11 @@ start_outgoing_proxy_if_needed() {
echo "cache_swap_high 0" >> "$SQUID_CONF"
# Disable logs
echo "access_log /tmp/test.log" >> "$SQUID_CONF"
echo "access_log none" >> "$SQUID_CONF"
echo "cache_store_log none" >> "$SQUID_CONF"
echo "cache_log /dev/null" >> "$SQUID_CONF"
echo "logfile_rotate 0" >> "$SQUID_CONF"
echo "debug_options ALL,1" >> "$SQUID_CONF"
echo "debug_options ALL,0" >> "$SQUID_CONF"
# Make it pass-through like
echo "forwarded_for delete" >> "$SQUID_CONF"
@@ -65,6 +65,10 @@ start_outgoing_proxy_if_needed() {
echo "request_header_access Via deny all" >> "$SQUID_CONF"
echo "request_header_access Cache-Control deny all" >> "$SQUID_CONF"
# Allow only local sources
echo "acl localhost_src src 127.0.0.1" >> "$SQUID_CONF"
echo "http_access deny !localhost_src" >> "$SQUID_CONF"
# Blocked IPs
if [[ -n "${OUTGOING_BLOCKED_IPS:-}" ]]; then
IFS=',' read -ra BLOCKED_IPS <<< "$OUTGOING_BLOCKED_IPS"

View File

@@ -1 +1 @@
module.exports = '2.0.2';
module.exports = '2.1.0';