Compare commits
5 Commits
AUT-979
...
custom-dep
Author | SHA1 | Date | |
---|---|---|---|
![]() |
76296532cf | ||
![]() |
10290ce6e3 | ||
![]() |
a2df7c1e89 | ||
![]() |
b89ba1e623 | ||
![]() |
4d0481a3c6 |
@@ -2,7 +2,6 @@ import appConfig from '../../src/config/app.js';
|
|||||||
import logger from '../../src/helpers/logger.js';
|
import logger from '../../src/helpers/logger.js';
|
||||||
import client from './client.js';
|
import client from './client.js';
|
||||||
import User from '../../src/models/user.js';
|
import User from '../../src/models/user.js';
|
||||||
import Config from '../../src/models/config.js';
|
|
||||||
import Role from '../../src/models/role.js';
|
import Role from '../../src/models/role.js';
|
||||||
import '../../src/config/orm.js';
|
import '../../src/config/orm.js';
|
||||||
import process from 'process';
|
import process from 'process';
|
||||||
@@ -46,8 +45,6 @@ export async function createUser(
|
|||||||
if (userCount === 0) {
|
if (userCount === 0) {
|
||||||
const user = await User.query().insertAndFetch(userParams);
|
const user = await User.query().insertAndFetch(userParams);
|
||||||
logger.info(`User has been saved: ${user.email}`);
|
logger.info(`User has been saved: ${user.email}`);
|
||||||
|
|
||||||
await Config.markInstallationCompleted();
|
|
||||||
} else {
|
} else {
|
||||||
logger.info('No need to seed a user.');
|
logger.info('No need to seed a user.');
|
||||||
}
|
}
|
||||||
|
@@ -1,29 +0,0 @@
|
|||||||
import defineAction from '../../../../helpers/define-action.js';
|
|
||||||
|
|
||||||
export default defineAction({
|
|
||||||
name: 'Create a watch',
|
|
||||||
key: 'createWatch',
|
|
||||||
description: 'Creates a new change detection watch for a specific website.',
|
|
||||||
arguments: [
|
|
||||||
{
|
|
||||||
label: 'URL',
|
|
||||||
key: 'url',
|
|
||||||
type: 'string',
|
|
||||||
required: true,
|
|
||||||
variables: true,
|
|
||||||
description: 'Url you want to monitor',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
|
|
||||||
async run($) {
|
|
||||||
const url = $.step.parameters.url;
|
|
||||||
|
|
||||||
const body = {
|
|
||||||
url,
|
|
||||||
};
|
|
||||||
|
|
||||||
const response = await $.http.post('/v1/watch', body);
|
|
||||||
|
|
||||||
$.setActionItem({ raw: response.data });
|
|
||||||
},
|
|
||||||
});
|
|
@@ -1,29 +0,0 @@
|
|||||||
import defineAction from '../../../../helpers/define-action.js';
|
|
||||||
|
|
||||||
export default defineAction({
|
|
||||||
name: 'Delete a watch',
|
|
||||||
key: 'deleteWatch',
|
|
||||||
description: 'Deletes a change detection watch.',
|
|
||||||
arguments: [
|
|
||||||
{
|
|
||||||
label: 'Watch ID',
|
|
||||||
key: 'watchId',
|
|
||||||
type: 'string',
|
|
||||||
required: true,
|
|
||||||
variables: true,
|
|
||||||
description: 'Watch id you want to delete',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
|
|
||||||
async run($) {
|
|
||||||
const watchId = $.step.parameters.watchId;
|
|
||||||
|
|
||||||
await $.http.delete(`/v1/watch/${watchId}`);
|
|
||||||
|
|
||||||
$.setActionItem({
|
|
||||||
raw: {
|
|
||||||
result: 'successful',
|
|
||||||
},
|
|
||||||
});
|
|
||||||
},
|
|
||||||
});
|
|
@@ -1,4 +0,0 @@
|
|||||||
import createWatch from './create-watch/index.js';
|
|
||||||
import deleteWatch from './delete-watch/index.js';
|
|
||||||
|
|
||||||
export default [createWatch, deleteWatch];
|
|
File diff suppressed because one or more lines are too long
Before Width: | Height: | Size: 9.9 KiB |
@@ -1,44 +0,0 @@
|
|||||||
import verifyCredentials from './verify-credentials.js';
|
|
||||||
import isStillVerified from './is-still-verified.js';
|
|
||||||
|
|
||||||
export default {
|
|
||||||
fields: [
|
|
||||||
{
|
|
||||||
key: 'screenName',
|
|
||||||
label: 'Screen Name',
|
|
||||||
type: 'string',
|
|
||||||
required: true,
|
|
||||||
readOnly: false,
|
|
||||||
value: null,
|
|
||||||
placeholder: null,
|
|
||||||
description:
|
|
||||||
'Screen name of your connection to be used on Automatisch UI.',
|
|
||||||
clickToCopy: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'instanceUrl',
|
|
||||||
label: 'Instance URL',
|
|
||||||
type: 'string',
|
|
||||||
required: true,
|
|
||||||
readOnly: false,
|
|
||||||
value: null,
|
|
||||||
placeholder: null,
|
|
||||||
description: null,
|
|
||||||
clickToCopy: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'apiKey',
|
|
||||||
label: 'API Key',
|
|
||||||
type: 'string',
|
|
||||||
required: true,
|
|
||||||
readOnly: false,
|
|
||||||
value: null,
|
|
||||||
placeholder: null,
|
|
||||||
description: 'Changedetection API key of your account.',
|
|
||||||
clickToCopy: false,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
|
|
||||||
verifyCredentials,
|
|
||||||
isStillVerified,
|
|
||||||
};
|
|
@@ -1,8 +0,0 @@
|
|||||||
import verifyCredentials from './verify-credentials.js';
|
|
||||||
|
|
||||||
const isStillVerified = async ($) => {
|
|
||||||
await verifyCredentials($);
|
|
||||||
return true;
|
|
||||||
};
|
|
||||||
|
|
||||||
export default isStillVerified;
|
|
@@ -1,10 +0,0 @@
|
|||||||
const verifyCredentials = async ($) => {
|
|
||||||
await $.http.get('/v1/systeminfo');
|
|
||||||
|
|
||||||
await $.auth.set({
|
|
||||||
screenName: $.auth.data.screenName,
|
|
||||||
apiKey: $.auth.data.apiKey,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
export default verifyCredentials;
|
|
@@ -1,9 +0,0 @@
|
|||||||
const addAuthHeader = ($, requestConfig) => {
|
|
||||||
if ($.auth.data?.apiKey) {
|
|
||||||
requestConfig.headers['x-api-key'] = $.auth.data.apiKey;
|
|
||||||
}
|
|
||||||
|
|
||||||
return requestConfig;
|
|
||||||
};
|
|
||||||
|
|
||||||
export default addAuthHeader;
|
|
@@ -1,10 +0,0 @@
|
|||||||
const setBaseUrl = ($, requestConfig) => {
|
|
||||||
const instanceUrl = $.auth.data.instanceUrl;
|
|
||||||
if (instanceUrl) {
|
|
||||||
requestConfig.baseURL = `${instanceUrl}/api`;
|
|
||||||
}
|
|
||||||
|
|
||||||
return requestConfig;
|
|
||||||
};
|
|
||||||
|
|
||||||
export default setBaseUrl;
|
|
@@ -1,19 +0,0 @@
|
|||||||
import defineApp from '../../helpers/define-app.js';
|
|
||||||
import addAuthHeader from './common/add-auth-header.js';
|
|
||||||
import auth from './auth/index.js';
|
|
||||||
import setBaseUrl from './common/set-base-url.js';
|
|
||||||
import actions from './actions/index.js';
|
|
||||||
|
|
||||||
export default defineApp({
|
|
||||||
name: 'Changedetection',
|
|
||||||
key: 'changedetection',
|
|
||||||
iconUrl: '{BASE_URL}/apps/changedetection/assets/favicon.svg',
|
|
||||||
authDocUrl: '{DOCS_URL}/apps/changedetection/connection',
|
|
||||||
supportsConnections: true,
|
|
||||||
baseUrl: 'https://changedetection.io',
|
|
||||||
apiBaseUrl: '',
|
|
||||||
primaryColor: '3056d3',
|
|
||||||
beforeRequest: [setBaseUrl, addAuthHeader],
|
|
||||||
auth,
|
|
||||||
actions,
|
|
||||||
});
|
|
@@ -1,10 +1,11 @@
|
|||||||
import { describe, it, expect, beforeEach } from 'vitest';
|
import { vi, describe, it, expect, beforeEach } from 'vitest';
|
||||||
import request from 'supertest';
|
import request from 'supertest';
|
||||||
import app from '../../../../../app';
|
import app from '../../../../../app';
|
||||||
import createAuthTokenByUserId from '../../../../../helpers/create-auth-token-by-user-id';
|
import createAuthTokenByUserId from '../../../../../helpers/create-auth-token-by-user-id';
|
||||||
import { createRole } from '../../../../../../test/factories/role';
|
import { createRole } from '../../../../../../test/factories/role';
|
||||||
import { createUser } from '../../../../../../test/factories/user';
|
import { createUser } from '../../../../../../test/factories/user';
|
||||||
import getUsersMock from '../../../../../../test/mocks/rest/api/v1/admin/users/get-users.js';
|
import getUsersMock from '../../../../../../test/mocks/rest/api/v1/admin/users/get-users.js';
|
||||||
|
import * as license from '../../../../../helpers/license.ee.js';
|
||||||
|
|
||||||
describe('GET /api/v1/admin/users', () => {
|
describe('GET /api/v1/admin/users', () => {
|
||||||
let currentUser, currentUserRole, anotherUser, anotherUserRole, token;
|
let currentUser, currentUserRole, anotherUser, anotherUserRole, token;
|
||||||
@@ -31,6 +32,8 @@ describe('GET /api/v1/admin/users', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should return users data', async () => {
|
it('should return users data', async () => {
|
||||||
|
vi.spyOn(license, 'hasValidLicense').mockResolvedValue(true);
|
||||||
|
|
||||||
const response = await request(app)
|
const response = await request(app)
|
||||||
.get('/api/v1/admin/users')
|
.get('/api/v1/admin/users')
|
||||||
.set('Authorization', token)
|
.set('Authorization', token)
|
||||||
|
@@ -1,9 +1,12 @@
|
|||||||
import User from '../../../../../models/user.js';
|
import User from '../../../../../models/user.js';
|
||||||
|
import Config from '../../../../../models/config.js';
|
||||||
|
|
||||||
export default async (request, response) => {
|
export default async (request, response) => {
|
||||||
const { email, password, fullName } = request.body;
|
const { email, password, fullName } = request.body;
|
||||||
|
|
||||||
await User.createAdmin({ email, password, fullName });
|
await User.createAdminUser({ email, password, fullName });
|
||||||
|
|
||||||
|
await Config.markInstallationCompleted();
|
||||||
|
|
||||||
response.status(204).end();
|
response.status(204).end();
|
||||||
};
|
};
|
||||||
|
@@ -4,7 +4,6 @@ import app from '../../../../../app.js';
|
|||||||
import Config from '../../../../../models/config.js';
|
import Config from '../../../../../models/config.js';
|
||||||
import User from '../../../../../models/user.js';
|
import User from '../../../../../models/user.js';
|
||||||
import { createRole } from '../../../../../../test/factories/role';
|
import { createRole } from '../../../../../../test/factories/role';
|
||||||
import { createUser } from '../../../../../../test/factories/user';
|
|
||||||
import { createInstallationCompletedConfig } from '../../../../../../test/factories/config';
|
import { createInstallationCompletedConfig } from '../../../../../../test/factories/config';
|
||||||
|
|
||||||
describe('POST /api/v1/installation/users', () => {
|
describe('POST /api/v1/installation/users', () => {
|
||||||
@@ -18,7 +17,7 @@ describe('POST /api/v1/installation/users', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('for incomplete installations', () => {
|
describe('for incomplete installations', () => {
|
||||||
it('should respond with HTTP 204 with correct payload when no user', async () => {
|
it('should respond with HTTP 204 with correct payload', async () => {
|
||||||
expect(await Config.isInstallationCompleted()).toBe(false);
|
expect(await Config.isInstallationCompleted()).toBe(false);
|
||||||
|
|
||||||
await request(app)
|
await request(app)
|
||||||
@@ -35,27 +34,6 @@ describe('POST /api/v1/installation/users', () => {
|
|||||||
expect(user.roleId).toBe(adminRole.id);
|
expect(user.roleId).toBe(adminRole.id);
|
||||||
expect(await Config.isInstallationCompleted()).toBe(true);
|
expect(await Config.isInstallationCompleted()).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should respond with HTTP 403 with correct payload when one user exists at least', async () => {
|
|
||||||
expect(await Config.isInstallationCompleted()).toBe(false);
|
|
||||||
|
|
||||||
await createUser();
|
|
||||||
|
|
||||||
const usersCountBefore = await User.query().resultSize();
|
|
||||||
|
|
||||||
await request(app)
|
|
||||||
.post('/api/v1/installation/users')
|
|
||||||
.send({
|
|
||||||
email: 'user@automatisch.io',
|
|
||||||
password: 'password',
|
|
||||||
fullName: 'Initial admin'
|
|
||||||
})
|
|
||||||
.expect(403);
|
|
||||||
|
|
||||||
const usersCountAfter = await User.query().resultSize();
|
|
||||||
|
|
||||||
expect(usersCountBefore).toEqual(usersCountAfter);
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('for completed installations', () => {
|
describe('for completed installations', () => {
|
||||||
|
@@ -1,16 +0,0 @@
|
|||||||
import Config from '../models/config.js';
|
|
||||||
import User from '../models/user.js';
|
|
||||||
|
|
||||||
export async function allowInstallation(request, response, next) {
|
|
||||||
if (await Config.isInstallationCompleted()) {
|
|
||||||
return response.status(403).end();
|
|
||||||
}
|
|
||||||
|
|
||||||
const hasAnyUsers = await User.query().resultSize() > 0;
|
|
||||||
|
|
||||||
if (hasAnyUsers) {
|
|
||||||
return response.status(403).end();
|
|
||||||
}
|
|
||||||
|
|
||||||
next();
|
|
||||||
};
|
|
9
packages/backend/src/helpers/authorize-installation.js
Normal file
9
packages/backend/src/helpers/authorize-installation.js
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
import Config from '../models/config.js';
|
||||||
|
|
||||||
|
export async function authorizeInstallation(request, response, next) {
|
||||||
|
if (await Config.isInstallationCompleted()) {
|
||||||
|
return response.status(403).end();
|
||||||
|
} else {
|
||||||
|
next();
|
||||||
|
}
|
||||||
|
};
|
@@ -10,7 +10,6 @@ import Base from './base.js';
|
|||||||
import App from './app.js';
|
import App from './app.js';
|
||||||
import AccessToken from './access-token.js';
|
import AccessToken from './access-token.js';
|
||||||
import Connection from './connection.js';
|
import Connection from './connection.js';
|
||||||
import Config from './config.js';
|
|
||||||
import Execution from './execution.js';
|
import Execution from './execution.js';
|
||||||
import Flow from './flow.js';
|
import Flow from './flow.js';
|
||||||
import Identity from './identity.ee.js';
|
import Identity from './identity.ee.js';
|
||||||
@@ -374,7 +373,7 @@ class User extends Base {
|
|||||||
return apps;
|
return apps;
|
||||||
}
|
}
|
||||||
|
|
||||||
static async createAdmin({ email, password, fullName }) {
|
static async createAdminUser({ email, password, fullName }) {
|
||||||
const adminRole = await Role.findAdmin();
|
const adminRole = await Role.findAdmin();
|
||||||
|
|
||||||
const adminUser = await this.query().insert({
|
const adminUser = await this.query().insert({
|
||||||
@@ -384,8 +383,6 @@ class User extends Base {
|
|||||||
roleId: adminRole.id
|
roleId: adminRole.id
|
||||||
});
|
});
|
||||||
|
|
||||||
await Config.markInstallationCompleted();
|
|
||||||
|
|
||||||
return adminUser;
|
return adminUser;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -2,17 +2,25 @@ import { Router } from 'express';
|
|||||||
import asyncHandler from 'express-async-handler';
|
import asyncHandler from 'express-async-handler';
|
||||||
import { authenticateUser } from '../../../../helpers/authentication.js';
|
import { authenticateUser } from '../../../../helpers/authentication.js';
|
||||||
import { authorizeAdmin } from '../../../../helpers/authorization.js';
|
import { authorizeAdmin } from '../../../../helpers/authorization.js';
|
||||||
|
import { checkIsEnterprise } from '../../../../helpers/check-is-enterprise.js';
|
||||||
import getUsersAction from '../../../../controllers/api/v1/admin/users/get-users.ee.js';
|
import getUsersAction from '../../../../controllers/api/v1/admin/users/get-users.ee.js';
|
||||||
import getUserAction from '../../../../controllers/api/v1/admin/users/get-user.ee.js';
|
import getUserAction from '../../../../controllers/api/v1/admin/users/get-user.ee.js';
|
||||||
|
|
||||||
const router = Router();
|
const router = Router();
|
||||||
|
|
||||||
router.get('/', authenticateUser, authorizeAdmin, asyncHandler(getUsersAction));
|
router.get(
|
||||||
|
'/',
|
||||||
|
authenticateUser,
|
||||||
|
authorizeAdmin,
|
||||||
|
checkIsEnterprise,
|
||||||
|
asyncHandler(getUsersAction)
|
||||||
|
);
|
||||||
|
|
||||||
router.get(
|
router.get(
|
||||||
'/:userId',
|
'/:userId',
|
||||||
authenticateUser,
|
authenticateUser,
|
||||||
authorizeAdmin,
|
authorizeAdmin,
|
||||||
|
checkIsEnterprise,
|
||||||
asyncHandler(getUserAction)
|
asyncHandler(getUserAction)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@@ -1,13 +1,13 @@
|
|||||||
import { Router } from 'express';
|
import { Router } from 'express';
|
||||||
import asyncHandler from 'express-async-handler';
|
import asyncHandler from 'express-async-handler';
|
||||||
import { allowInstallation } from '../../../../helpers/allow-installation.js';
|
import { authorizeInstallation } from '../../../../helpers/authorize-installation.js';
|
||||||
import createUserAction from '../../../../controllers/api/v1/installation/users/create-user.js';
|
import createUserAction from '../../../../controllers/api/v1/installation/users/create-user.js';
|
||||||
|
|
||||||
const router = Router();
|
const router = Router();
|
||||||
|
|
||||||
router.post(
|
router.post(
|
||||||
'/',
|
'/',
|
||||||
allowInstallation,
|
authorizeInstallation,
|
||||||
asyncHandler(createUserAction)
|
asyncHandler(createUserAction)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@@ -41,14 +41,6 @@ export default defineConfig({
|
|||||||
{ text: 'Connection', link: '/apps/carbone/connection' },
|
{ text: 'Connection', link: '/apps/carbone/connection' },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
|
||||||
text: 'Changedetection',
|
|
||||||
collapsible: true,
|
|
||||||
collapsed: true,
|
|
||||||
items: [
|
|
||||||
{ text: 'Connection', link: '/apps/changedetection/connection' },
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
text: 'Datastore',
|
text: 'Datastore',
|
||||||
collapsible: true,
|
collapsible: true,
|
||||||
|
@@ -1,14 +0,0 @@
|
|||||||
---
|
|
||||||
favicon: /favicons/changedetection.svg
|
|
||||||
items:
|
|
||||||
- name: Create a watch
|
|
||||||
desc: Creates a new change detection watch for a specific website.
|
|
||||||
- name: Delete a watch
|
|
||||||
desc: Deletes a change detection watch.
|
|
||||||
---
|
|
||||||
|
|
||||||
<script setup>
|
|
||||||
import CustomListing from '../../components/CustomListing.vue'
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<CustomListing />
|
|
@@ -1,14 +0,0 @@
|
|||||||
# Changedetection
|
|
||||||
|
|
||||||
:::info
|
|
||||||
This page explains the steps you need to follow to set up the Changedetection
|
|
||||||
connection in Automatisch. If any of the steps are outdated, please let us know!
|
|
||||||
:::
|
|
||||||
|
|
||||||
1. Go to your Changedetection admin panel.
|
|
||||||
2. Click on the **Settings** button.
|
|
||||||
3. Click on the **API** tab.
|
|
||||||
4. Copy **API key** from the page to the `API Key` field on Automatisch.
|
|
||||||
5. Add your Instance URL in the **Instance URL** field on Automatisch.
|
|
||||||
6. Write any screen name to be displayed in Automatisch.
|
|
||||||
7. Now, you can start using the Changedetection connection with Automatisch.
|
|
@@ -3,7 +3,6 @@
|
|||||||
The following integrations are currently supported by Automatisch.
|
The following integrations are currently supported by Automatisch.
|
||||||
|
|
||||||
- [Carbone](/apps/carbone/actions)
|
- [Carbone](/apps/carbone/actions)
|
||||||
- [Changedetection](/apps/changedetection/actions)
|
|
||||||
- [Datastore](/apps/datastore/actions)
|
- [Datastore](/apps/datastore/actions)
|
||||||
- [DeepL](/apps/deepl/actions)
|
- [DeepL](/apps/deepl/actions)
|
||||||
- [Delay](/apps/delay/actions)
|
- [Delay](/apps/delay/actions)
|
||||||
|
File diff suppressed because one or more lines are too long
Before Width: | Height: | Size: 9.9 KiB |
File diff suppressed because one or more lines are too long
Before Width: | Height: | Size: 9.9 KiB |
Reference in New Issue
Block a user