Skip to content

Commit fd1bf5c

Browse files
authored
build: Release (#2664)
2 parents 408154b + 0393a4b commit fd1bf5c

18 files changed

+575
-210
lines changed

CHANGELOG.md

-15
Original file line numberDiff line numberDiff line change
@@ -15,19 +15,6 @@ Details:
1515
- Purpose: official release
1616
- Suitable environment: production
1717

18-
## ⚠️ [Beta Releases][log_beta]
19-
20-
These are releases that are pretty stable, but may have still some bugs to be fixed before official release.
21-
22-
> ### “Please try out, we’d love your feedback!”
23-
24-
Details:
25-
- Stability: *pretty stable*
26-
- NPM channel: `@beta`
27-
- Branch: [beta][branch_beta]
28-
- Purpose: feature maturation
29-
- Suitable environment: development
30-
3118
## 🔥 [Alpha Releases][log_alpha]
3219

3320
> ### “If you are curious to see what's next!”
@@ -43,8 +30,6 @@ Details:
4330

4431

4532
[log_release]: https://github.com/parse-community/parse-dashboard/blob/release/changelogs/CHANGELOG_release.md
46-
[log_beta]: https://github.com/parse-community/parse-dashboard/blob/beta/changelogs/CHANGELOG_beta.md
4733
[log_alpha]: https://github.com/parse-community/parse-dashboard/blob/alpha/changelogs/CHANGELOG_alpha.md
4834
[branch_release]: https://github.com/parse-community/parse-dashboard/tree/release
49-
[branch_beta]: https://github.com/parse-community/parse-dashboard/tree/beta
5035
[branch_alpha]: https://github.com/parse-community/parse-dashboard/tree/alpha

Dockerfile

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
############################################################
22
# Build stage
33
############################################################
4-
FROM node:20.17.0-alpine3.20 AS build
4+
FROM node:20.18.2-alpine3.20 AS build
55

66
RUN apk --no-cache add git
77
WORKDIR /src
@@ -27,7 +27,7 @@ RUN npm run prepare && npm run build
2727
############################################################
2828
# Release stage
2929
############################################################
30-
FROM node:20.17.0-alpine3.20 AS release
30+
FROM node:20.18.2-alpine3.20 AS release
3131
WORKDIR /src
3232

3333
# Copy production node_modules

Parse-Dashboard/app.js

+32-12
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ const packageJson = require('package-json');
55
const csrf = require('csurf');
66
const Authentication = require('./Authentication.js');
77
const fs = require('fs');
8+
const ConfigKeyCache = require('./configKeyCache.js');
89

910
const currentVersionFeatures = require('../package.json').parseDashboardFeatures;
1011

@@ -80,11 +81,11 @@ module.exports = function(config, options) {
8081
});
8182

8283
// Serve the configuration.
83-
app.get('/parse-dashboard-config.json', function(req, res) {
84+
app.get('/parse-dashboard-config.json', async (req, res) => {
8485
const apps = config.apps.map((app) => Object.assign({}, app)); // make a copy
8586
const response = {
86-
apps: apps,
87-
newFeaturesInLatestVersion: newFeaturesInLatestVersion,
87+
apps,
88+
newFeaturesInLatestVersion,
8889
};
8990

9091
//Based on advice from Doug Wilson here:
@@ -119,20 +120,31 @@ module.exports = function(config, options) {
119120
return app;
120121
});
121122
}
122-
123123
if (successfulAuth) {
124124
if (appsUserHasAccess) {
125-
// Restric access to apps defined in user dictionary
126-
// If they didn't supply any app id, user will access all apps
127-
response.apps = response.apps.filter(function (app) {
128-
return appsUserHasAccess.find(appUserHasAccess => {
129-
const isSame = app.appId === appUserHasAccess.appId;
130-
if (isSame && appUserHasAccess.readOnly) {
125+
const processedApps = await Promise.all(
126+
response.apps.map(async (app) => {
127+
const matchingAccess = appsUserHasAccess.find(
128+
(access) => access.appId === app.appId
129+
);
130+
131+
if (!matchingAccess) {
132+
return null;
133+
}
134+
135+
if (matchingAccess.readOnly) {
131136
app.masterKey = app.readOnlyMasterKey;
132137
}
133-
return isSame;
138+
139+
if (typeof app.masterKey === 'function') {
140+
app.masterKey = await ConfigKeyCache.get(app.appId, 'masterKey', app.masterKeyTtl, app.masterKey);
141+
}
142+
143+
return app;
134144
})
135-
});
145+
);
146+
147+
response.apps = processedApps.filter((app) => app !== null);
136148
}
137149
// They provided correct auth
138150
return res.json(response);
@@ -147,6 +159,14 @@ module.exports = function(config, options) {
147159
//(ie. didn't supply usernames and passwords)
148160
if (requestIsLocal || options.dev) {
149161
//Allow no-auth access on localhost only, if they have configured the dashboard to not need auth
162+
await Promise.all(
163+
response.apps.map(async (app) => {
164+
if (typeof app.masterKey === 'function') {
165+
app.masterKey = await ConfigKeyCache.get(app.appId, 'masterKey', app.masterKeyTtl, app.masterKey);
166+
}
167+
})
168+
);
169+
150170
return res.json(response);
151171
}
152172
//We shouldn't get here. Fail closed.

Parse-Dashboard/configKeyCache.js

+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
class KeyCache {
2+
constructor() {
3+
this.cache = {};
4+
}
5+
6+
async get(appId, key, ttl, callback) {
7+
key = `${appId}:${key}`;
8+
const cached = this.cache[key];
9+
if (cached && cached.expiry > Date.now()) {
10+
return cached.value;
11+
}
12+
13+
const value = await Promise.resolve(callback());
14+
this.cache[key] = {
15+
value,
16+
expiry: Date.now() + ttl,
17+
};
18+
return value;
19+
}
20+
}
21+
22+
module.exports = new KeyCache();

Parse-Dashboard/index.js

+1
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ const startServer = require('./server');
1313
const program = require('commander');
1414
program.option('--appId [appId]', 'the app Id of the app you would like to manage.');
1515
program.option('--masterKey [masterKey]', 'the master key of the app you would like to manage.');
16+
program.option('--masterKeyTtl [masterKeyTtl]', 'the master key ttl of the app you would like to manage.');
1617
program.option('--serverURL [serverURL]', 'the server url of the app you would like to manage.');
1718
program.option('--graphQLServerURL [graphQLServerURL]', 'the GraphQL server url of the app you would like to manage.');
1819
program.option('--dev', 'Enable development mode. This will disable authentication and allow non HTTPS connections. DO NOT ENABLE IN PRODUCTION SERVERS');

README.md

+60-9
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,13 @@
33
---
44

55
[![Build Status](https://github.com/parse-community/parse-dashboard/workflows/ci/badge.svg?branch=alpha)](https://github.com/parse-community/parse-dashboard/actions?query=workflow%3Aci+branch%3Aalpha)
6-
[![Build Status](https://github.com/parse-community/parse-dashboard/workflows/ci/badge.svg?branch=beta)](https://github.com/parse-community/parse-dashboard/actions?query=workflow%3Aci+branch%3Abeta)
76
[![Build Status](https://github.com/parse-community/parse-dashboard/workflows/ci/badge.svg?branch=release)](https://github.com/parse-community/parse-dashboard/actions?query=workflow%3Aci+branch%3Arelease)
87
[![Snyk Badge](https://snyk.io/test/github/parse-community/parse-dashboard/badge.svg)](https://snyk.io/test/github/parse-community/parse-dashboard)
98

109
[![Node Version](https://img.shields.io/badge/nodejs-18,_20-green.svg?logo=node.js&style=flat)](https://nodejs.org/)
1110
[![auto-release](https://img.shields.io/badge/%F0%9F%9A%80-auto--release-9e34eb.svg)](https://github.com/parse-community/parse-dashboard/releases)
1211

1312
[![npm latest version](https://img.shields.io/npm/v/parse-dashboard/latest.svg)](https://www.npmjs.com/package/parse-dashboard)
14-
[![npm beta version](https://img.shields.io/npm/v/parse-dashboard/beta.svg)](https://www.npmjs.com/package/parse-dashboard)
1513
[![npm alpha version](https://img.shields.io/npm/v/parse-dashboard/alpha.svg)](https://www.npmjs.com/package/parse-dashboard)
1614

1715
[![Backers on Open Collective](https://opencollective.com/parse-server/backers/badge.svg)][open-collective-link]
@@ -71,6 +69,7 @@ Parse Dashboard is a standalone dashboard for managing your [Parse Server](https
7169
- [Video Item](#video-item)
7270
- [Audio Item](#audio-item)
7371
- [Button Item](#button-item)
72+
- [Panel Item](#panel-item)
7473
- [Browse as User](#browse-as-user)
7574
- [Change Pointer Key](#change-pointer-key)
7675
- [Limitations](#limitations)
@@ -130,6 +129,11 @@ Parse Dashboard is continuously tested with the most recent releases of Node.js
130129
| Parameter | Type | Optional | Default | Example | Description |
131130
|----------------------------------------|---------------------|----------|---------|----------------------|---------------------------------------------------------------------------------------------------------------------------------------------|
132131
| `apps` | Array<Object> | no | - | `[{ ... }, { ... }]` | The apps that are configured for the dashboard. |
132+
| `apps.appId` | String | yes | - | `"myAppId"` | The Application ID for your Parse Server instance. |
133+
| `apps.masterKey` | String \| Function | yes | - | `"exampleMasterKey"`, `() => "exampleMasterKey"` | The master key for full access to Parse Server. It can be provided directly as a String or as a Function returning a String. |
134+
| `apps.masterKeyTtl` | Number | no | - | `3600` | Time-to-live (TTL) for the master key in seconds. This defines how long the master key is cached before the `masterKey` function is re-triggered. |
135+
| `apps.serverURL` | String | yes | - | `"http://localhost:1337/parse"` | The URL where your Parse Server is running. |
136+
| `apps.appName` | String | no | - | `"MyApp"` | The display name of the app in the dashboard. |
133137
| `infoPanel` | Array<Object> | yes | - | `[{ ... }, { ... }]` | The [info panel](#info-panel) configuration. |
134138
| `infoPanel[*].title` | String | no | - | `User Details` | The panel title. |
135139
| `infoPanel[*].classes` | Array<String> | no | - | `["_User"]` | The classes for which the info panel should be displayed. |
@@ -929,12 +933,13 @@ Example:
929933

930934
A text item that consists of a key and a value. The value can optionally be linked to a URL.
931935

932-
| Parameter | Value | Optional | Description |
933-
|-----------|--------|----------|-----------------------------------------------------------------------------------|
934-
| `type` | String | No | Must be `"keyValue"`. |
935-
| `key` | String | No | The key text to display. |
936-
| `value` | String | No | The value text to display. |
937-
| `url` | String | Yes | The URL that will be opened in a new browser tab when clicking on the value text. |
936+
| Parameter | Value | Default | Optional | Description |
937+
|-----------------|---------|-------------|----------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
938+
| `type` | String | - | No | Must be `"keyValue"`. |
939+
| `key` | String | - | No | The key text to display. |
940+
| `value` | String | - | No | The value text to display. |
941+
| `url` | String | `undefined` | Yes | The URL that will be opened in a new browser tab when clicking on the value text. It can be set to an absolute URL or a relative URL in which case the base URL is `<PROTOCOL>://<HOST>/<MOUNT_PATH>/`. |
942+
| `isRelativeUrl` | Boolean | `false` | Yes | Set this to `true` when linking to another dashboard page, in which case the base URL for the relative URL will be `<PROTOCOL>://<HOST>/<MOUNT_PATH>/apps/<APP_NAME>/`. |
938943

939944
Examples:
940945

@@ -951,7 +956,33 @@ Examples:
951956
"type": "keyValue",
952957
"key": "Last purchase ID",
953958
"value": "123",
954-
"url": "https://example.com/purchaseDetails?purchaseId=012345"
959+
"url": "https://example.com/purchaseDetails?purchaseId=123"
960+
}
961+
```
962+
963+
```json
964+
{
965+
"type": "keyValue",
966+
"key": "Purchase",
967+
"value": "123",
968+
"url": "browser/Purchase",
969+
"isRelativeUrl": true
970+
}
971+
```
972+
973+
To navigate to a specific object using a relative URL, the query parameters must be URL encoded:
974+
975+
```js
976+
const objectId = 'abc123';
977+
const className = 'Purchase';
978+
const query = [{ field: 'objectId', constraint: 'eq', compareTo: objectId }];
979+
const url = `browser/Purchase?filters=${JSON.stringify(query)}`;
980+
const item = {
981+
type: 'keyValue',
982+
key: 'Purchase',
983+
value: objectId,
984+
url,
985+
isRelativeUrl: true
955986
}
956987
```
957988

@@ -1082,6 +1113,26 @@ Example:
10821113
}
10831114
```
10841115

1116+
#### Panel Item
1117+
1118+
A sub-panel whose data is loaded on-demand by expanding the item.
1119+
1120+
| Parameter | Value | Optional | Description |
1121+
|---------------------|--------|----------|---------------------------------------------------------------------------------------------------------------------------------------------------|
1122+
| `type` | String | No | Must be `"infoPanel"`. |
1123+
| `title` | String | No | The title to display in the expandable headline. |
1124+
| `cloudCodeFunction` | String | No | The Cloud Code Function to call which receives the selected object in the data browser and returns the response to be displayed in the sub-panel. |
1125+
1126+
Example:
1127+
1128+
```json
1129+
{
1130+
"type": "panel",
1131+
"title": "Purchase History",
1132+
"cloudCodeFunction": "getUserPurchaseHistory"
1133+
}
1134+
```
1135+
10851136
## Browse as User
10861137

10871138
▶️ *Core > Browser > Browse*

0 commit comments

Comments
 (0)