Compare commits
19
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fdef87e3b9 | ||
|
|
964498dbaa | ||
|
|
5b519cb62f | ||
|
|
11fc3b4485 | ||
|
|
37c0b59f82 | ||
|
|
239611ad51 | ||
|
|
0575e665fe | ||
|
|
019766dced | ||
|
|
6dc9e4ed99 | ||
|
|
d4b64b90fc | ||
|
|
1a49826b85 | ||
|
|
815643eb2c | ||
|
|
bcc4c0d923 | ||
|
|
cf7810b067 | ||
|
|
f1c72df8b6 | ||
|
|
5056d38848 | ||
|
|
3ef5b3ead8 | ||
|
|
ded58dc8b0 | ||
|
|
d8ee1ecdd1 |
@@ -2,6 +2,11 @@ name: Build and push Docker DEV image
|
||||
|
||||
on:
|
||||
push:
|
||||
paths-ignore:
|
||||
- '.github/**'
|
||||
- 'charts/**'
|
||||
- 'docker-*.sh'
|
||||
- '*.md'
|
||||
branches: [master]
|
||||
|
||||
jobs:
|
||||
|
||||
@@ -38,5 +38,6 @@ jobs:
|
||||
uses: helm/chart-releaser-action@v1.5.0
|
||||
with:
|
||||
charts_dir: charts
|
||||
mark_as_latest: false
|
||||
env:
|
||||
CR_TOKEN: "${{ secrets.GITHUB_TOKEN }}"
|
||||
|
||||
+12
-17
@@ -1,27 +1,22 @@
|
||||
FROM node:lts-alpine
|
||||
FROM node:18-alpine
|
||||
|
||||
ARG ALPINE_VERSION=3.16
|
||||
ARG VIPS_VERSION=8.13.3
|
||||
ARG VIPS_VERSION=8.14.5
|
||||
|
||||
RUN apk -U upgrade \
|
||||
&& apk add \
|
||||
bash giflib glib lcms2 libexif \
|
||||
libgsf libjpeg-turbo libpng librsvg libwebp \
|
||||
orc pango tiff \
|
||||
--repository https://alpine.global.ssl.fastly.net/alpine/v${ALPINE_VERSION}/community/ \
|
||||
--repository https://alpine.global.ssl.fastly.net/alpine/v${ALPINE_VERSION}/main/ \
|
||||
bash pkgconf \
|
||||
libjpeg-turbo libexif librsvg cgif tiff libspng libimagequant \
|
||||
--no-cache \
|
||||
&& apk add \
|
||||
build-base giflib-dev glib-dev lcms2-dev libexif-dev \
|
||||
libgsf-dev libjpeg-turbo-dev libpng-dev librsvg-dev libwebp-dev \
|
||||
orc-dev pango-dev tiff-dev \
|
||||
build-base gobject-introspection-dev meson \
|
||||
libjpeg-turbo-dev libexif-dev librsvg-dev cgif-dev tiff-dev libspng-dev libimagequant-dev \
|
||||
--virtual vips-dependencies \
|
||||
--repository https://alpine.global.ssl.fastly.net/alpine/v${ALPINE_VERSION}/community/ \
|
||||
--repository https://alpine.global.ssl.fastly.net/alpine/v${ALPINE_VERSION}/main/ \
|
||||
--no-cache \
|
||||
&& wget -O- https://github.com/libvips/libvips/releases/download/v${VIPS_VERSION}/vips-${VIPS_VERSION}.tar.gz | tar xzC /tmp \
|
||||
&& wget -O- https://github.com/libvips/libvips/releases/download/v${VIPS_VERSION}/vips-${VIPS_VERSION}.tar.xz | tar xJC /tmp \
|
||||
&& cd /tmp/vips-${VIPS_VERSION} \
|
||||
&& ./configure \
|
||||
&& make \
|
||||
&& make install-strip \
|
||||
&& meson setup build-dir \
|
||||
&& cd build-dir \
|
||||
&& ninja \
|
||||
&& ninja test \
|
||||
&& ninja install \
|
||||
&& rm -rf /tmp/vips-${VIPS_VERSION}
|
||||
|
||||
@@ -20,10 +20,9 @@
|
||||
|
||||
## How to deploy Planka
|
||||
|
||||
There are 2 types of installation:
|
||||
- [Without Docker](https://docs.planka.cloud/docs/installl-planka/Debian%20&%20Ubuntu) ([for Windows](https://docs.planka.cloud/docs/installl-planka/Windows))
|
||||
- [Dockerized](https://docs.planka.cloud/docs/installl-planka/Docker%20Compose)
|
||||
- [Automated installation](https://github.com/plankanban/planka-installer)
|
||||
There are many ways to install Planka
|
||||
|
||||
[Check them out](https://docs.planka.cloud/docs/intro)
|
||||
|
||||
For configuration, please see the [configuration section](https://docs.planka.cloud/docs/category/configuration).
|
||||
|
||||
|
||||
@@ -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: 0.1.6
|
||||
version: 0.1.11
|
||||
|
||||
# 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: "1.14.1"
|
||||
appVersion: "1.15.0"
|
||||
|
||||
dependencies:
|
||||
- alias: postgresql
|
||||
|
||||
+23
-1
@@ -21,7 +21,11 @@ git clone https://github.com/Chris-Greaves/planka-helm-chart.git
|
||||
cd planka-helm-chart
|
||||
helm dependency build
|
||||
export SECRETKEY=$(openssl rand -hex 64)
|
||||
helm install planka . --set secretkey=$SECRETKEY
|
||||
helm install planka . --set secretkey=$SECRETKEY \
|
||||
--set admin_email="demo@demo.demo" \
|
||||
--set admin_password="demo" \
|
||||
--set admin_name="Demo Demo" \
|
||||
--set admin_username="demo"
|
||||
```
|
||||
|
||||
> **NOTE:** The command `openssl rand -hex 64` is needed to create a random hexadecimal key for planka. On Windows you can use Git Bash to run that command.
|
||||
@@ -39,11 +43,19 @@ To access Planka externally you can use the following configuration
|
||||
```bash
|
||||
# HTTP only
|
||||
helm install planka . --set secretkey=$SECRETKEY \
|
||||
--set admin_email="demo@demo.demo" \
|
||||
--set admin_password="demo" \
|
||||
--set admin_name="Demo Demo" \
|
||||
--set admin_username="demo"
|
||||
--set ingress.enabled=true \
|
||||
--set ingress.hosts[0].host=planka.example.dev \
|
||||
|
||||
# HTTPS
|
||||
helm install planka . --set secretkey=$SECRETKEY \
|
||||
--set admin_email="demo@demo.demo" \
|
||||
--set admin_password="demo" \
|
||||
--set admin_name="Demo Demo" \
|
||||
--set admin_username="demo"
|
||||
--set ingress.enabled=true \
|
||||
--set ingress.hosts[0].host=planka.example.dev \
|
||||
--set ingress.tls[0].secretName=planka-tls \
|
||||
@@ -54,6 +66,16 @@ or create a values.yaml file like:
|
||||
|
||||
```yaml
|
||||
secretkey: "<InsertSecretKey>"
|
||||
# The admin section needs to be present for new instances of Planka, after the first start you can remove the lines starting with admin_. If you want the admin user to be unchangeable admin_email: has to stay
|
||||
# After changing the config you have to run ```helm upgrade planka . -f values.yaml```
|
||||
|
||||
# Admin user
|
||||
admin_email: "demo@demo.demo" # Do not remove if you want to prevent this user from being edited/deleted
|
||||
admin_password: "demo"
|
||||
admin_name: "Demo Demo"
|
||||
admin_username: "demo"
|
||||
# Admin user
|
||||
|
||||
ingress:
|
||||
enabled: true
|
||||
hosts:
|
||||
|
||||
@@ -35,7 +35,7 @@ spec:
|
||||
imagePullPolicy: {{ .Values.image.pullPolicy }}
|
||||
ports:
|
||||
- name: http
|
||||
containerPort: {{ .Values.service.port }}
|
||||
containerPort: {{ .Values.service.containerPort | default 1337 }}
|
||||
protocol: TCP
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
@@ -75,10 +75,45 @@ spec:
|
||||
value: {{ required "A secret key needs to be generated using 'openssl rand -hex 64' and assigned to secretkey." .Values.secretkey }}
|
||||
- name: TRUST_PROXY
|
||||
value: "0"
|
||||
- name: DEFAULT_ADMIN_EMAIL
|
||||
value: {{ .Values.admin_email }}
|
||||
- name: DEFAULT_ADMIN_PASSWORD
|
||||
value: {{ .Values.admin_password }}
|
||||
- name: DEFAULT_ADMIN_NAME
|
||||
value: {{ .Values.admin_name }}
|
||||
- name: DEFAULT_ADMIN_USERNAME
|
||||
value: {{ .Values.admin_username }}
|
||||
{{ range $k, $v := .Values.env }}
|
||||
- name: {{ $k | quote }}
|
||||
value: {{ $v | quote }}
|
||||
{{- end }}
|
||||
{{- if .Values.oidc.enabled }}
|
||||
{{- $secretName := default (printf "%s-oidc" (include "planka.fullname" .)) .Values.oidc.existingSecret }}
|
||||
- name: OIDC_CLIENT_ID
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
key: clientId
|
||||
name: {{ $secretName }}
|
||||
- name: OIDC_CLIENT_SECRET
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
key: clientSecret
|
||||
name: {{ $secretName }}
|
||||
- name: OIDC_ISSUER
|
||||
value: {{ required "issuerUrl is required when configuring OIDC" .Values.oidc.issuerUrl | quote }}
|
||||
- name: OIDC_SCOPES
|
||||
value: {{ join " " .Values.oidc.scopes | default "openid profile email" | quote }}
|
||||
{{- if .Values.oidc.admin.roles }}
|
||||
- name: OIDC_ADMIN_ROLES
|
||||
value: {{ join "," .Values.oidc.admin.roles | quote }}
|
||||
{{- end }}
|
||||
- name: OIDC_ROLES_ATTRIBUTE
|
||||
value: {{ .Values.oidc.admin.rolesAttribute | default "groups" | quote }}
|
||||
{{- if .Values.oidc.admin.ignoreRoles }}
|
||||
- name: OIDC_IGNORE_ROLES
|
||||
value: {{ .Values.oidc.admin.ignoreRoles | quote }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- with .Values.nodeSelector }}
|
||||
nodeSelector:
|
||||
{{- toYaml . | nindent 8 }}
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
{{- if .Values.oidc.enabled }}
|
||||
{{- if eq (and (not (empty .Values.oidc.clientId)) (not (empty .Values.oidc.clientSecret))) (not (empty .Values.oidc.existingSecret)) -}}
|
||||
{{- fail "Either specify inline `clientId` and `clientSecret` or refer to them via `existingSecret`" -}}
|
||||
{{- end }}
|
||||
{{- if (and (and (not (empty .Values.oidc.clientId)) (not (empty .Values.oidc.clientSecret))) (empty .Values.oidc.existingSecret)) -}}
|
||||
apiVersion: v1
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: {{ include "planka.fullname" . }}-oidc
|
||||
labels:
|
||||
{{- include "planka.labels" . | nindent 4 }}
|
||||
type: Opaque
|
||||
data:
|
||||
clientId: {{ .Values.oidc.clientId | b64enc | quote }}
|
||||
clientSecret: {{ .Values.oidc.clientSecret | b64enc | quote }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
@@ -46,6 +46,10 @@ securityContext: {}
|
||||
service:
|
||||
type: ClusterIP
|
||||
port: 1337
|
||||
## @param service.containerPort Planka HTTP container port
|
||||
## If empty will default to 1337
|
||||
##
|
||||
containerPort: 1337
|
||||
|
||||
ingress:
|
||||
enabled: false
|
||||
@@ -109,3 +113,68 @@ persistence:
|
||||
|
||||
accessMode: ReadWriteOnce
|
||||
size: 10Gi
|
||||
|
||||
## OpenID Identity Management configuration
|
||||
##
|
||||
## Example:
|
||||
## ---------------
|
||||
## oidc:
|
||||
## enabled: true
|
||||
## clientId: sxxaAIAxVXlCxTmc1YLHBbQr8NL8MqLI2DUbt42d
|
||||
## clientSecret: om4RTMRVHRszU7bqxB7RZNkHIzA8e4sGYWxeCwIMYQXPwEBWe4SY5a0wwCe9ltB3zrq5f0dnFnp34cEHD7QSMHsKvV9AiV5Z7eqDraMnv0I8IFivmuV5wovAECAYreSI
|
||||
## issuerUrl: https://auth.local/application/o/planka/
|
||||
## admin:
|
||||
## roles:
|
||||
## - planka-admin
|
||||
##
|
||||
## ---------------
|
||||
## NOTE: A minimal configuration requires setting `clientId`, `clientSecret` and `issuerUrl`. (plus `admin.roles` for administrators)
|
||||
## ref: https://docs.planka.cloud/docs/Configuration/OIDC
|
||||
##
|
||||
oidc:
|
||||
## @param oidc.enabled Enable single sign-on (SSO) with OpenID Connect (OIDC)
|
||||
##
|
||||
enabled: false
|
||||
|
||||
## OIDC credentials
|
||||
## @param oidc.clientId A string unique to the provider that identifies your app.
|
||||
## @param oidc.clientSecret A secret string that the provider uses to confirm ownership of a client ID.
|
||||
##
|
||||
## NOTE: Either specify inline `clientId` and `clientSecret` or refer to them via `existingSecret`
|
||||
##
|
||||
clientId: ""
|
||||
clientSecret: ""
|
||||
|
||||
## @param oidc.existingSecret Name of an existing secret containing OIDC credentials
|
||||
## NOTE: Must contain key `clientId` and `clientSecret`
|
||||
## NOTE: When it's set, the `clientId` and `clientSecret` parameters are ignored
|
||||
##
|
||||
existingSecret: ""
|
||||
|
||||
## @param oidc.issuerUrl The OpenID connect metadata document endpoint
|
||||
##
|
||||
issuerUrl: ""
|
||||
|
||||
## @param oidc.scopes A list of scopes required for OIDC client.
|
||||
## If empty will default to `openid`, `profile` and `email`
|
||||
## NOTE: Planka needs the email and name claims
|
||||
##
|
||||
scopes: []
|
||||
|
||||
## Admin permissions configuration
|
||||
admin:
|
||||
## @param oidc.admin.ignoreRoles If set to true, the admin roles will be ignored.
|
||||
## It is useful if you want to use OIDC for authentication but not for authorization.
|
||||
## If empty will default to `false`
|
||||
##
|
||||
ignoreRoles: false
|
||||
|
||||
## @param oidc.admin.rolesAttribute The name of a custom group claim that you have configured in your OIDC provider
|
||||
## If empty will default to `groups`
|
||||
##
|
||||
rolesAttribute: groups
|
||||
|
||||
## @param oidc.admin.roles The names of the admin groups
|
||||
##
|
||||
roles: []
|
||||
# - planka-admin
|
||||
|
||||
+1
-1
@@ -1 +1 @@
|
||||
REACT_APP_VERSION=1.14.1
|
||||
REACT_APP_VERSION=1.15.0
|
||||
|
||||
Generated
+3895
-22103
File diff suppressed because it is too large
Load Diff
+31
-31
@@ -55,60 +55,60 @@
|
||||
"dependencies": {
|
||||
"@juggle/resize-observer": "^3.4.0",
|
||||
"classnames": "^2.3.2",
|
||||
"date-fns": "^2.29.3",
|
||||
"date-fns": "^2.30.0",
|
||||
"dequal": "^2.0.3",
|
||||
"easymde": "^2.18.0",
|
||||
"history": "^5.3.0",
|
||||
"i18next": "^22.0.6",
|
||||
"i18next-browser-languagedetector": "^7.0.1",
|
||||
"i18next": "^23.7.6",
|
||||
"i18next-browser-languagedetector": "^7.2.0",
|
||||
"initials": "^3.1.2",
|
||||
"js-cookie": "^3.0.1",
|
||||
"jwt-decode": "^3.1.2",
|
||||
"js-cookie": "^3.0.5",
|
||||
"jwt-decode": "^4.0.0",
|
||||
"lodash": "^4.17.21",
|
||||
"nanoid": "^5.0.2",
|
||||
"node-sass": "^8.0.0",
|
||||
"photoswipe": "^5.3.3",
|
||||
"nanoid": "^5.0.3",
|
||||
"node-sass": "^9.0.0",
|
||||
"photoswipe": "^5.4.2",
|
||||
"prop-types": "^15.8.1",
|
||||
"react": "^18.2.0",
|
||||
"react-app-rewired": "^2.2.1",
|
||||
"react-beautiful-dnd": "^13.1.1",
|
||||
"react-datepicker": "^4.8.0",
|
||||
"react-datepicker": "^4.21.0",
|
||||
"react-dom": "^18.2.0",
|
||||
"react-dropzone": "^14.2.3",
|
||||
"react-i18next": "^12.0.0",
|
||||
"react-i18next": "^13.5.0",
|
||||
"react-input-mask": "^2.0.4",
|
||||
"react-markdown": "^8.0.3",
|
||||
"react-photoswipe-gallery": "^2.2.2",
|
||||
"react-redux": "^8.0.5",
|
||||
"react-router-dom": "^6.4.3",
|
||||
"react-markdown": "^8.0.7",
|
||||
"react-photoswipe-gallery": "^2.2.7",
|
||||
"react-redux": "^8.1.3",
|
||||
"react-router-dom": "^6.19.0",
|
||||
"react-scripts": "5.0.1",
|
||||
"react-simplemde-editor": "^5.2.0",
|
||||
"react-textarea-autosize": "^8.4.0",
|
||||
"redux": "^4.2.0",
|
||||
"react-textarea-autosize": "^8.5.3",
|
||||
"redux": "^4.2.1",
|
||||
"redux-logger": "^3.0.6",
|
||||
"redux-orm": "^0.16.2",
|
||||
"redux-saga": "^1.2.1",
|
||||
"remark-breaks": "^3.0.2",
|
||||
"remark-gfm": "^3.0.1",
|
||||
"reselect": "^4.1.7",
|
||||
"redux-saga": "^1.2.3",
|
||||
"remark-breaks": "^4.0.0",
|
||||
"remark-gfm": "^4.0.0",
|
||||
"reselect": "^4.1.8",
|
||||
"sails.io.js": "^1.2.1",
|
||||
"semantic-ui-react": "^2.1.3",
|
||||
"semantic-ui-react": "^2.1.4",
|
||||
"socket.io-client": "^2.5.0",
|
||||
"validator": "^13.7.0",
|
||||
"whatwg-fetch": "^3.6.2",
|
||||
"validator": "^13.11.0",
|
||||
"whatwg-fetch": "^3.6.19",
|
||||
"zxcvbn": "^4.4.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@testing-library/jest-dom": "^5.16.5",
|
||||
"@testing-library/react": "^13.4.0",
|
||||
"@testing-library/user-event": "^13.5.0",
|
||||
"@testing-library/jest-dom": "^6.1.4",
|
||||
"@testing-library/react": "^14.1.0",
|
||||
"@testing-library/user-event": "^14.5.1",
|
||||
"babel-preset-airbnb": "^5.0.0",
|
||||
"chai": "^4.3.7",
|
||||
"eslint": "^8.28.0",
|
||||
"chai": "^4.3.10",
|
||||
"eslint": "^8.53.0",
|
||||
"eslint-config-airbnb": "^19.0.4",
|
||||
"eslint-plugin-import": "^2.26.0",
|
||||
"eslint-plugin-jsx-a11y": "^6.6.1",
|
||||
"eslint-plugin-react": "^7.31.11",
|
||||
"eslint-plugin-import": "^2.29.0",
|
||||
"eslint-plugin-jsx-a11y": "^6.8.0",
|
||||
"eslint-plugin-react": "^7.33.2",
|
||||
"eslint-plugin-react-hooks": "^4.6.0",
|
||||
"react-test-renderer": "^18.2.0"
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import ActionTypes from '../constants/ActionTypes';
|
||||
|
||||
const initializeCore = (
|
||||
config,
|
||||
user,
|
||||
board,
|
||||
users,
|
||||
@@ -21,7 +20,6 @@ const initializeCore = (
|
||||
) => ({
|
||||
type: ActionTypes.CORE_INITIALIZE,
|
||||
payload: {
|
||||
config,
|
||||
user,
|
||||
board,
|
||||
users,
|
||||
@@ -41,6 +39,14 @@ const initializeCore = (
|
||||
},
|
||||
});
|
||||
|
||||
// TODO: with success?
|
||||
initializeCore.fetchConfig = (config) => ({
|
||||
type: ActionTypes.CORE_INITIALIZE__CONFIG_FETCH,
|
||||
payload: {
|
||||
config,
|
||||
},
|
||||
});
|
||||
|
||||
const logout = () => ({
|
||||
type: ActionTypes.LOGOUT,
|
||||
payload: {},
|
||||
|
||||
@@ -217,6 +217,7 @@ const CardModal = React.memo(
|
||||
onUserSelect={onUserAdd}
|
||||
onUserDeselect={onUserRemove}
|
||||
>
|
||||
{/* eslint-disable-next-line jsx-a11y/control-has-associated-label */}
|
||||
<button
|
||||
type="button"
|
||||
className={classNames(styles.attachment, styles.dueDate)}
|
||||
@@ -266,6 +267,7 @@ const CardModal = React.memo(
|
||||
onMove={onLabelMove}
|
||||
onDelete={onLabelDelete}
|
||||
>
|
||||
{/* eslint-disable-next-line jsx-a11y/control-has-associated-label */}
|
||||
<button
|
||||
type="button"
|
||||
className={classNames(styles.attachment, styles.dueDate)}
|
||||
@@ -314,10 +316,11 @@ const CardModal = React.memo(
|
||||
)}
|
||||
</span>
|
||||
{canEdit && (
|
||||
// eslint-disable-next-line jsx-a11y/control-has-associated-label
|
||||
<button
|
||||
onClick={handleToggleStopwatchClick}
|
||||
type="button"
|
||||
className={classNames(styles.attachment, styles.dueDate)}
|
||||
onClick={handleToggleStopwatchClick}
|
||||
>
|
||||
<Icon
|
||||
name={stopwatch.startedAt ? 'pause' : 'play'}
|
||||
|
||||
@@ -48,6 +48,8 @@ const Tasks = React.memo(({ items, canEdit, onCreate, onUpdate, onMove, onDelete
|
||||
return (
|
||||
<>
|
||||
{items.length > 0 && (
|
||||
<>
|
||||
<span className={styles.progressWrapper}>
|
||||
<Progress
|
||||
autoSuccess
|
||||
value={completedItems.length}
|
||||
@@ -56,6 +58,11 @@ const Tasks = React.memo(({ items, canEdit, onCreate, onUpdate, onMove, onDelete
|
||||
size="tiny"
|
||||
className={styles.progress}
|
||||
/>
|
||||
</span>
|
||||
<span className={styles.count}>
|
||||
{completedItems.length}/{items.length}
|
||||
</span>
|
||||
</>
|
||||
)}
|
||||
<DragDropContext onDragStart={handleDragStart} onDragEnd={handleDragEnd}>
|
||||
<Droppable droppableId="tasks" type={DroppableTypes.TASK}>
|
||||
|
||||
@@ -3,6 +3,23 @@
|
||||
margin: 0 0 16px;
|
||||
}
|
||||
|
||||
.progressWrapper {
|
||||
display: inline-block;
|
||||
padding: 3px 0;
|
||||
vertical-align: top;
|
||||
width: calc(100% - 50px);
|
||||
}
|
||||
|
||||
.count {
|
||||
color: #8c8c8c;
|
||||
display: inline-block;
|
||||
font-size: 14px;
|
||||
line-height: 14px;
|
||||
text-align: right;
|
||||
vertical-align: top;
|
||||
width: 50px;
|
||||
}
|
||||
|
||||
.taskButton {
|
||||
background: transparent;
|
||||
border: none;
|
||||
|
||||
@@ -155,7 +155,7 @@ const ActionsStep = React.memo(
|
||||
</Menu.Item>
|
||||
</>
|
||||
)}
|
||||
{!user.isLockedAdmin && (
|
||||
{!user.isDeletionLocked && (
|
||||
<Menu.Item className={styles.menuItem} onClick={handleDeleteClick}>
|
||||
{t('action.deleteUser', {
|
||||
context: 'title',
|
||||
|
||||
@@ -18,7 +18,8 @@ const Item = React.memo(
|
||||
phone,
|
||||
isAdmin,
|
||||
isLocked,
|
||||
isLockedAdmin,
|
||||
isRoleLocked,
|
||||
isDeletionLocked,
|
||||
emailUpdateForm,
|
||||
passwordUpdateForm,
|
||||
usernameUpdateForm,
|
||||
@@ -48,7 +49,7 @@ const Item = React.memo(
|
||||
<Table.Cell>{username || '-'}</Table.Cell>
|
||||
<Table.Cell>{email}</Table.Cell>
|
||||
<Table.Cell>
|
||||
<Radio toggle checked={isAdmin} disabled={isLockedAdmin} onChange={handleIsAdminChange} />
|
||||
<Radio toggle checked={isAdmin} disabled={isRoleLocked} onChange={handleIsAdminChange} />
|
||||
</Table.Cell>
|
||||
<Table.Cell textAlign="right">
|
||||
<ActionsPopup
|
||||
@@ -60,7 +61,7 @@ const Item = React.memo(
|
||||
phone,
|
||||
isAdmin,
|
||||
isLocked,
|
||||
isLockedAdmin,
|
||||
isDeletionLocked,
|
||||
emailUpdateForm,
|
||||
passwordUpdateForm,
|
||||
usernameUpdateForm,
|
||||
@@ -93,7 +94,8 @@ Item.propTypes = {
|
||||
phone: PropTypes.string,
|
||||
isAdmin: PropTypes.bool.isRequired,
|
||||
isLocked: PropTypes.bool.isRequired,
|
||||
isLockedAdmin: PropTypes.bool.isRequired,
|
||||
isRoleLocked: PropTypes.bool.isRequired,
|
||||
isDeletionLocked: PropTypes.bool.isRequired,
|
||||
/* eslint-disable react/forbid-prop-types */
|
||||
emailUpdateForm: PropTypes.object.isRequired,
|
||||
passwordUpdateForm: PropTypes.object.isRequired,
|
||||
|
||||
@@ -111,7 +111,8 @@ const UsersModal = React.memo(
|
||||
phone={item.phone}
|
||||
isAdmin={item.isAdmin}
|
||||
isLocked={item.isLocked}
|
||||
isLockedAdmin={item.isLockedAdmin}
|
||||
isRoleLocked={item.isRoleLocked}
|
||||
isDeletionLocked={item.isDeletionLocked}
|
||||
emailUpdateForm={item.emailUpdateForm}
|
||||
passwordUpdateForm={item.passwordUpdateForm}
|
||||
usernameUpdateForm={item.usernameUpdateForm}
|
||||
|
||||
@@ -24,6 +24,7 @@ export default {
|
||||
/* Core */
|
||||
|
||||
CORE_INITIALIZE: 'CORE_INITIALIZE',
|
||||
CORE_INITIALIZE__CONFIG_FETCH: 'CORE_INITIALIZE__CONFIG_FETCH',
|
||||
LOGOUT: 'LOGOUT',
|
||||
LOGOUT__ACCESS_TOKEN_INVALIDATE: 'LOGOUT__ACCESS_TOKEN_INVALIDATE',
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ export default {
|
||||
time: 'HH:mm',
|
||||
dateTime: '$t(format:date) $t(format:time)',
|
||||
longDate: 'MMMMd日',
|
||||
longDateTime: "MMMMd'日 ' HH:MM",
|
||||
longDateTime: "MMMMd'日 ' HH:mm",
|
||||
},
|
||||
|
||||
translation: {
|
||||
|
||||
@@ -45,7 +45,8 @@ export default class extends BaseModel {
|
||||
subscribeToOwnCards: attr(),
|
||||
isAdmin: attr(),
|
||||
isLocked: attr(),
|
||||
isLockedAdmin: attr(),
|
||||
isRoleLocked: attr(),
|
||||
isDeletionLocked: attr(),
|
||||
deletedAt: attr(),
|
||||
createdAt: attr({
|
||||
getDefault: () => new Date(),
|
||||
|
||||
@@ -24,9 +24,11 @@ export default (state = initialState, { type, payload }) => {
|
||||
return {
|
||||
...state,
|
||||
isInitializing: false,
|
||||
...(payload.config && {
|
||||
};
|
||||
case ActionTypes.CORE_INITIALIZE__CONFIG_FETCH:
|
||||
return {
|
||||
...state,
|
||||
config: payload.config,
|
||||
}),
|
||||
};
|
||||
default:
|
||||
return state;
|
||||
|
||||
@@ -14,6 +14,8 @@ export function* initializeCore() {
|
||||
let config;
|
||||
if (!currentConfig) {
|
||||
({ item: config } = yield call(api.getConfig)); // TODO: handle error
|
||||
|
||||
yield put(actions.initializeCore.fetchConfig(config));
|
||||
}
|
||||
|
||||
const {
|
||||
@@ -40,7 +42,6 @@ export function* initializeCore() {
|
||||
|
||||
yield put(
|
||||
actions.initializeCore(
|
||||
config,
|
||||
user,
|
||||
board,
|
||||
users,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import Cookies from 'js-cookie';
|
||||
import jwtDecode from 'jwt-decode';
|
||||
import { jwtDecode } from 'jwt-decode';
|
||||
|
||||
import Config from '../constants/Config';
|
||||
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
version: '3'
|
||||
|
||||
services:
|
||||
postgres:
|
||||
image: postgres:alpine
|
||||
restart: unless-stopped
|
||||
volumes:
|
||||
- db-data:/var/lib/postgresql/data
|
||||
ports:
|
||||
- 5432:5432
|
||||
environment:
|
||||
- POSTGRES_DB=planka
|
||||
- POSTGRES_HOST_AUTH_METHOD=trust
|
||||
|
||||
volumes:
|
||||
db-data:
|
||||
+52
-3
@@ -1,16 +1,65 @@
|
||||
version: '3'
|
||||
|
||||
services:
|
||||
planka:
|
||||
image: ghcr.io/plankanban/planka:dev
|
||||
command: >
|
||||
bash -c
|
||||
"for i in `seq 1 30`; do
|
||||
./start.sh &&
|
||||
s=$$? && break || s=$$?;
|
||||
echo \"Tried $$i times. Waiting 5 seconds...\";
|
||||
sleep 5;
|
||||
done; (exit $$s)"
|
||||
restart: unless-stopped
|
||||
volumes:
|
||||
- user-avatars:/app/public/user-avatars
|
||||
- project-background-images:/app/public/project-background-images
|
||||
- attachments:/app/private/attachments
|
||||
ports:
|
||||
- 3000:1337
|
||||
environment:
|
||||
- BASE_URL=http://localhost:3000
|
||||
- DATABASE_URL=postgresql://postgres@postgres/planka
|
||||
- SECRET_KEY=notsecretkey
|
||||
|
||||
# - TRUST_PROXY=0
|
||||
# - TOKEN_EXPIRES_IN=365 # In days
|
||||
|
||||
# related: https://github.com/knex/knex/issues/2354
|
||||
# As knex does not pass query parameters from the connection string we
|
||||
# have to use environment variables in order to pass the desired values, e.g.
|
||||
# - PGSSLMODE=<value>
|
||||
|
||||
# Configure knex to accept SSL certificates
|
||||
# - KNEX_REJECT_UNAUTHORIZED_SSL_CERTIFICATE=false
|
||||
|
||||
# - DEFAULT_ADMIN_EMAIL=demo@demo.demo # Do not remove if you want to prevent this user from being edited/deleted
|
||||
# - DEFAULT_ADMIN_PASSWORD=demo
|
||||
# - DEFAULT_ADMIN_NAME=Demo Demo
|
||||
# - DEFAULT_ADMIN_USERNAME=demo
|
||||
|
||||
# - OIDC_ISSUER=
|
||||
# - OIDC_CLIENT_ID=
|
||||
# - OIDC_CLIENT_SECRET=
|
||||
# - OIDC_SCOPES=openid email profile
|
||||
# - OIDC_ADMIN_ROLES=admin
|
||||
# - OIDC_ROLES_ATTRIBUTE=groups
|
||||
# - OIDC_IGNORE_ROLES=true
|
||||
depends_on:
|
||||
- postgres
|
||||
|
||||
postgres:
|
||||
image: postgres:alpine
|
||||
image: postgres:14-alpine
|
||||
restart: unless-stopped
|
||||
volumes:
|
||||
- db-data:/var/lib/postgresql/data
|
||||
ports:
|
||||
- 5432:5432
|
||||
environment:
|
||||
- POSTGRES_DB=planka
|
||||
- POSTGRES_HOST_AUTH_METHOD=trust
|
||||
|
||||
volumes:
|
||||
user-avatars:
|
||||
project-background-images:
|
||||
attachments:
|
||||
db-data:
|
||||
|
||||
+8
-6
@@ -20,15 +20,11 @@ services:
|
||||
- 3000:1337
|
||||
environment:
|
||||
- BASE_URL=http://localhost:3000
|
||||
- TRUST_PROXY=0
|
||||
- DATABASE_URL=postgresql://postgres@postgres/planka
|
||||
- SECRET_KEY=notsecretkey
|
||||
|
||||
# Can be removed after installation
|
||||
- DEFAULT_ADMIN_EMAIL=demo@demo.demo # Do not remove if you want to prevent this user from being edited/deleted
|
||||
- DEFAULT_ADMIN_PASSWORD=demo
|
||||
- DEFAULT_ADMIN_NAME=Demo Demo
|
||||
- DEFAULT_ADMIN_USERNAME=demo
|
||||
# - TRUST_PROXY=0
|
||||
# - TOKEN_EXPIRES_IN=365 # In days
|
||||
|
||||
# related: https://github.com/knex/knex/issues/2354
|
||||
# As knex does not pass query parameters from the connection string we
|
||||
@@ -38,12 +34,18 @@ services:
|
||||
# Configure knex to accept SSL certificates
|
||||
# - KNEX_REJECT_UNAUTHORIZED_SSL_CERTIFICATE=false
|
||||
|
||||
# - DEFAULT_ADMIN_EMAIL=demo@demo.demo # Do not remove if you want to prevent this user from being edited/deleted
|
||||
# - DEFAULT_ADMIN_PASSWORD=demo
|
||||
# - DEFAULT_ADMIN_NAME=Demo Demo
|
||||
# - DEFAULT_ADMIN_USERNAME=demo
|
||||
|
||||
# - OIDC_ISSUER=
|
||||
# - OIDC_CLIENT_ID=
|
||||
# - OIDC_CLIENT_SECRET=
|
||||
# - OIDC_SCOPES=openid email profile
|
||||
# - OIDC_ADMIN_ROLES=admin
|
||||
# - OIDC_ROLES_ATTRIBUTE=groups
|
||||
# - OIDC_IGNORE_ROLES=true
|
||||
depends_on:
|
||||
- postgres
|
||||
|
||||
|
||||
Generated
+1383
-826
File diff suppressed because it is too large
Load Diff
+8
-8
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "planka",
|
||||
"version": "1.14.1",
|
||||
"version": "1.15.0",
|
||||
"private": true,
|
||||
"homepage": "https://plankanban.github.io/planka",
|
||||
"repository": {
|
||||
@@ -56,14 +56,14 @@
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"concurrently": "^7.6.0",
|
||||
"husky": "^8.0.2",
|
||||
"lint-staged": "^13.0.3"
|
||||
"concurrently": "^8.2.2",
|
||||
"husky": "^8.0.3",
|
||||
"lint-staged": "^15.1.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"eslint": "^8.28.0",
|
||||
"eslint-config-prettier": "^8.5.0",
|
||||
"eslint-plugin-prettier": "^4.2.1",
|
||||
"prettier": "^2.7.1"
|
||||
"eslint": "^8.53.0",
|
||||
"eslint-config-prettier": "^9.0.0",
|
||||
"eslint-plugin-prettier": "^5.0.1",
|
||||
"prettier": "^3.1.0"
|
||||
}
|
||||
}
|
||||
|
||||
+6
-7
@@ -4,13 +4,6 @@ BASE_URL=http://localhost:1337
|
||||
DATABASE_URL=postgresql://postgres@localhost/planka
|
||||
SECRET_KEY=notsecretkey
|
||||
|
||||
## Can be removed after installation
|
||||
|
||||
DEFAULT_ADMIN_EMAIL=demo@demo.demo # Do not remove if you want to prevent this user from being edited/deleted
|
||||
DEFAULT_ADMIN_PASSWORD=demo
|
||||
DEFAULT_ADMIN_NAME=Demo Demo
|
||||
DEFAULT_ADMIN_USERNAME=demo
|
||||
|
||||
## Optional
|
||||
|
||||
# TRUST_PROXY=0
|
||||
@@ -24,12 +17,18 @@ DEFAULT_ADMIN_USERNAME=demo
|
||||
# Configure knex to accept SSL certificates
|
||||
# KNEX_REJECT_UNAUTHORIZED_SSL_CERTIFICATE=false
|
||||
|
||||
# DEFAULT_ADMIN_EMAIL=demo@demo.demo # Do not remove if you want to prevent this user from being edited/deleted
|
||||
# DEFAULT_ADMIN_PASSWORD=demo
|
||||
# DEFAULT_ADMIN_NAME=Demo Demo
|
||||
# DEFAULT_ADMIN_USERNAME=demo
|
||||
|
||||
# OIDC_ISSUER=
|
||||
# OIDC_CLIENT_ID=
|
||||
# OIDC_CLIENT_SECRET=
|
||||
# OIDC_SCOPES=openid email profile
|
||||
# OIDC_ADMIN_ROLES=admin
|
||||
# OIDC_ROLES_ATTRIBUTE=groups
|
||||
# OIDC_IGNORE_ROLES=true
|
||||
|
||||
## Do not edit this
|
||||
|
||||
|
||||
@@ -73,6 +73,10 @@ module.exports = {
|
||||
delete inputs.name;
|
||||
/* eslint-enable no-param-reassign */
|
||||
} else if (user.isSso) {
|
||||
if (!sails.config.custom.oidcIgnoreRoles) {
|
||||
delete inputs.isAdmin; // eslint-disable-line no-param-reassign
|
||||
}
|
||||
|
||||
delete inputs.name; // eslint-disable-line no-param-reassign
|
||||
}
|
||||
|
||||
|
||||
@@ -90,7 +90,10 @@ module.exports = {
|
||||
});
|
||||
}
|
||||
|
||||
const updateFieldKeys = ['email', 'isAdmin', 'isSso', 'name', 'username'];
|
||||
const updateFieldKeys = ['email', 'isSso', 'name', 'username'];
|
||||
if (!sails.config.custom.oidcIgnoreRoles) {
|
||||
updateFieldKeys.push('isAdmin');
|
||||
}
|
||||
|
||||
const updateValues = {};
|
||||
// eslint-disable-next-line no-restricted-syntax
|
||||
|
||||
@@ -110,12 +110,13 @@ module.exports = {
|
||||
tableName: 'user_account',
|
||||
|
||||
customToJSON() {
|
||||
const isLockedAdmin = this.email === sails.config.custom.defaultAdminEmail;
|
||||
const isDefaultAdmin = this.email === sails.config.custom.defaultAdminEmail;
|
||||
|
||||
return {
|
||||
..._.omit(this, ['password', 'isSso', 'avatar', 'passwordChangedAt']),
|
||||
isLockedAdmin,
|
||||
isLocked: this.isSso || isLockedAdmin,
|
||||
isLocked: this.isSso || isDefaultAdmin,
|
||||
isRoleLocked: (this.isSso && !sails.config.custom.oidcIgnoreRoles) || isDefaultAdmin,
|
||||
isDeletionLocked: isDefaultAdmin,
|
||||
avatarUrl:
|
||||
this.avatar &&
|
||||
`${sails.config.custom.userAvatarsUrl}/${this.avatar.dirname}/square-100.${this.avatar.extension}`,
|
||||
|
||||
@@ -39,6 +39,7 @@ module.exports.custom = {
|
||||
oidcScopes: process.env.OIDC_SCOPES || 'openid email profile',
|
||||
oidcAdminRoles: process.env.OIDC_ADMIN_ROLES ? process.env.OIDC_ADMIN_ROLES.split(',') : [],
|
||||
oidcRolesAttribute: process.env.OIDC_ROLES_ATTRIBUTE || 'groups',
|
||||
oidcIgnoreRoles: process.env.OIDC_IGNORE_ROLES === 'true',
|
||||
|
||||
// TODO: move client base url to environment variable?
|
||||
oidcRedirectUri: `${
|
||||
|
||||
@@ -34,10 +34,6 @@ exports.seed = async (knex) => {
|
||||
createdAt: new Date().toISOString(),
|
||||
});
|
||||
} catch (error) {
|
||||
if (Object.keys(data).length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
await knex('user_account').update(data).where('email', process.env.DEFAULT_ADMIN_EMAIL);
|
||||
}
|
||||
};
|
||||
|
||||
Generated
+2309
-824
File diff suppressed because it is too large
Load Diff
+19
-19
@@ -27,38 +27,38 @@
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"bcrypt": "^5.1.0",
|
||||
"dotenv": "^16.0.3",
|
||||
"dotenv-cli": "^6.0.0",
|
||||
"bcrypt": "^5.1.1",
|
||||
"dotenv": "^16.3.1",
|
||||
"dotenv-cli": "^7.3.0",
|
||||
"filenamify": "^4.3.0",
|
||||
"jsonwebtoken": "^9.0.0",
|
||||
"knex": "^2.3.0",
|
||||
"jsonwebtoken": "^9.0.2",
|
||||
"knex": "^3.0.1",
|
||||
"lodash": "^4.17.21",
|
||||
"moment": "^2.29.4",
|
||||
"move-file": "^2.1.0",
|
||||
"openid-client": "^5.4.3",
|
||||
"rimraf": "^3.0.2",
|
||||
"openid-client": "^5.6.1",
|
||||
"rimraf": "^5.0.5",
|
||||
"sails": "^1.5.7",
|
||||
"sails-hook-orm": "^4.0.2",
|
||||
"sails-hook-sockets": "^2.0.3",
|
||||
"sails-hook-sockets": "^2.0.4",
|
||||
"sails-postgresql-redacted": "^1.0.2-9",
|
||||
"sharp": "^0.31.2",
|
||||
"sharp": "^0.32.6",
|
||||
"stream-to-array": "^2.3.0",
|
||||
"uuid": "^9.0.0",
|
||||
"validator": "^13.7.0",
|
||||
"winston": "^3.8.2",
|
||||
"uuid": "^9.0.1",
|
||||
"validator": "^13.11.0",
|
||||
"winston": "^3.11.0",
|
||||
"zxcvbn": "^4.4.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"chai": "^4.3.7",
|
||||
"eslint": "^8.28.0",
|
||||
"chai": "^4.3.10",
|
||||
"eslint": "^8.53.0",
|
||||
"eslint-config-airbnb-base": "^15.0.0",
|
||||
"eslint-plugin-import": "^2.26.0",
|
||||
"mocha": "^10.1.0",
|
||||
"nodemon": "^2.0.20",
|
||||
"supertest": "^6.3.1"
|
||||
"eslint-plugin-import": "^2.29.0",
|
||||
"mocha": "^10.2.0",
|
||||
"nodemon": "^3.0.1",
|
||||
"supertest": "^6.3.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^12.10"
|
||||
"node": ">=18"
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user