feat: Serve static assets of apps
This commit is contained in:
@@ -6,6 +6,7 @@ import corsOptions from './config/cors-options';
|
||||
import graphQLInstance from './helpers/graphql-instance';
|
||||
import logger from './helpers/logger';
|
||||
import morgan from './helpers/morgan';
|
||||
import appAssetsHandler from './helpers/app-assets-handler';
|
||||
import errorHandler from './helpers/error-handler';
|
||||
import './config/database';
|
||||
import authentication from './helpers/authentication';
|
||||
@@ -13,6 +14,8 @@ import authentication from './helpers/authentication';
|
||||
const app = express();
|
||||
const port = appConfig.port;
|
||||
|
||||
appAssetsHandler(app)
|
||||
|
||||
app.use(morgan);
|
||||
app.use(express.json());
|
||||
app.use(express.urlencoded({ extended: false }));
|
||||
|
5
packages/backend/src/apps/flickr/assets/favicon.svg
Normal file
5
packages/backend/src/apps/flickr/assets/favicon.svg
Normal file
@@ -0,0 +1,5 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" aria-label="Flickr" role="img" viewBox="0 0 512 512">
|
||||
<rect width="512" height="512" rx="15%" fill="#fff"/>
|
||||
<circle cx="157" cy="256" fill="#0063dc" r="79"/>
|
||||
<circle cx="355" cy="256" fill="#ff0084" r="79"/>
|
||||
</svg>
|
After Width: | Height: | Size: 261 B |
@@ -1,7 +1,10 @@
|
||||
{
|
||||
import appInfoType from '../../types/app-info';
|
||||
import appConfig from '../../config/app';
|
||||
|
||||
const appInfo: appInfoType = {
|
||||
"name": "Flickr",
|
||||
"key": "flickr",
|
||||
"iconUrl": "https://automatisch.io/apps/flickr.png",
|
||||
"iconUrl": `${appConfig.baseUrl}/apps/flickr/assets/favicon.svg`,
|
||||
"docUrl": "https://automatisch.io/docs/flickr",
|
||||
"primaryColor": "000000",
|
||||
"fields": [
|
||||
@@ -113,3 +116,5 @@
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
export default appInfo;
|
4
packages/backend/src/apps/twitch/assets/favicon.svg
Normal file
4
packages/backend/src/apps/twitch/assets/favicon.svg
Normal file
@@ -0,0 +1,4 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" aria-label="Twitch" role="img" viewBox="0 0 512 512">
|
||||
<rect width="512" height="512" rx="15%" fill="#fff"/>
|
||||
<path d="m115 101-22 56v228h78v42h44l41-42h63l85-85v-199zm260 185-48 48h-78l-42 42v-42h-65v-204h233zm-48-100v85h-30v-85zm-78 0v85h-29v-85z" fill="#6441a4"/>
|
||||
</svg>
|
After Width: | Height: | Size: 316 B |
@@ -1,7 +1,10 @@
|
||||
{
|
||||
import appInfoType from '../../types/app-info';
|
||||
import appConfig from '../../config/app';
|
||||
|
||||
const appInfo: appInfoType = {
|
||||
"name": "Twitch",
|
||||
"key": "twitch",
|
||||
"iconUrl": "https://automatisch.io/apps/twitch.png",
|
||||
"iconUrl": `${appConfig.baseUrl}/apps/twitch/assets/favicon.svg`,
|
||||
"docUrl": "https://automatisch.io/docs/twitch",
|
||||
"primaryColor": "6441a5",
|
||||
"fields": [
|
||||
@@ -43,3 +46,5 @@
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
export default appInfo;
|
4
packages/backend/src/apps/twitter/assets/favicon.svg
Normal file
4
packages/backend/src/apps/twitter/assets/favicon.svg
Normal file
@@ -0,0 +1,4 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" aria-label="Twitter" role="img" viewBox="0 0 512 512">
|
||||
<rect width="512" height="512" rx="15%" fill="#1da1f2"/>
|
||||
<path fill="#fff" d="M437 152a72 72 0 01-40 12a72 72 0 0032-40a72 72 0 01-45 17a72 72 0 00-122 65a200 200 0 01-145-74a72 72 0 0022 94a72 72 0 01-32-7a72 72 0 0056 69a72 72 0 01-32 1a72 72 0 0067 50a200 200 0 01-105 29a200 200 0 00309-179a200 200 0 0035-37"/>
|
||||
</svg>
|
After Width: | Height: | Size: 422 B |
@@ -1,7 +1,10 @@
|
||||
{
|
||||
import appInfoType from '../../types/app-info';
|
||||
import appConfig from '../../config/app';
|
||||
|
||||
const appInfo: appInfoType = {
|
||||
"name": "Twitter",
|
||||
"key": "twitter",
|
||||
"iconUrl": "https://automatisch.io/apps/twitter.png",
|
||||
"iconUrl": `${appConfig.baseUrl}/apps/twitter/assets/favicon.svg`,
|
||||
"docUrl": "https://automatisch.io/docs/twitter",
|
||||
"primaryColor": "2DAAE1",
|
||||
"fields": [
|
||||
@@ -113,3 +116,5 @@
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
export default appInfo;
|
@@ -11,7 +11,9 @@ type AppConfig = {
|
||||
postgresPort: number,
|
||||
postgresHost: string,
|
||||
postgresUsername: string,
|
||||
postgresPassword: string
|
||||
postgresPassword: string,
|
||||
baseUrl?: string
|
||||
webAppUrl?: string
|
||||
}
|
||||
|
||||
const appConfig: AppConfig = {
|
||||
@@ -27,4 +29,10 @@ const appConfig: AppConfig = {
|
||||
postgresPassword: process.env.POSTGRESS_PASSWORD,
|
||||
}
|
||||
|
||||
const webAppUrl = `${appConfig.protocol}://${appConfig.host}:${appConfig.corsPort}`;
|
||||
appConfig.webAppUrl = webAppUrl;
|
||||
|
||||
const baseUrl = `${appConfig.protocol}://${appConfig.host}:${appConfig.port}`;
|
||||
appConfig.baseUrl = baseUrl;
|
||||
|
||||
export default appConfig;
|
||||
|
@@ -1,7 +1,7 @@
|
||||
import appConfig from './app'
|
||||
|
||||
const corsOptions = {
|
||||
origin: `${appConfig.protocol}://${appConfig.host}:${appConfig.corsPort}`,
|
||||
origin: appConfig.webAppUrl,
|
||||
methods: 'POST',
|
||||
credentials: true,
|
||||
optionsSuccessStatus: 200,
|
||||
|
@@ -9,7 +9,7 @@ type Params = {
|
||||
}
|
||||
|
||||
const getConnectedAppsResolver = async (params: Params, req: RequestWithCurrentUser) => {
|
||||
let apps = App.findAll(params.name)
|
||||
let apps = await App.findAll(params.name)
|
||||
|
||||
const connections = await Connection.query()
|
||||
.select('connections.key')
|
||||
|
15
packages/backend/src/helpers/app-assets-handler.ts
Normal file
15
packages/backend/src/helpers/app-assets-handler.ts
Normal file
@@ -0,0 +1,15 @@
|
||||
import express, { Application } from 'express';
|
||||
import App from '../models/app';
|
||||
|
||||
const appAssetsHandler = async (app: Application) => {
|
||||
const appNames = App.list;
|
||||
|
||||
appNames.forEach(appName => {
|
||||
app.use(
|
||||
`/apps/${appName}/assets/favicon.svg`,
|
||||
express.static(`src/apps/${appName}/assets/favicon.svg`)
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
export default appAssetsHandler;
|
@@ -4,22 +4,28 @@ class App {
|
||||
static folderPath = __dirname + '/../apps';
|
||||
static list = fs.readdirSync(this.folderPath);
|
||||
|
||||
static findAll(name?: string): object[] {
|
||||
if(!name) return this.list.map((name) => this.findOneByName(name));
|
||||
static async findAll(name?: string): Promise<object[]> {
|
||||
let appList;
|
||||
|
||||
return this.list
|
||||
if(!name) {
|
||||
appList = this.list.map(async (name) => await this.findOneByName(name));
|
||||
} else {
|
||||
appList = this.list
|
||||
.filter((app) => app.includes(name.toLowerCase()))
|
||||
.map((name) => this.findOneByName(name));
|
||||
.map(async (name) => await this.findOneByName(name));
|
||||
}
|
||||
|
||||
static findOneByName(name: string): object {
|
||||
const rawAppData = fs.readFileSync(this.folderPath + `/${name}/info.json`, 'utf-8');
|
||||
return JSON.parse(rawAppData);
|
||||
return Promise.all(appList)
|
||||
}
|
||||
|
||||
static findOneByKey(key: string): object {
|
||||
const rawAppData = fs.readFileSync(this.folderPath + `/${key}/info.json`, 'utf-8');
|
||||
return JSON.parse(rawAppData);
|
||||
static async findOneByName(name: string): Promise<object> {
|
||||
const rawAppData = (await import(`../apps/${name}/info`)).default;
|
||||
return rawAppData;
|
||||
}
|
||||
|
||||
static async findOneByKey(key: string): Promise<object> {
|
||||
const rawAppData = (await import(`../apps/${key}/info`)).default;
|
||||
return rawAppData;
|
||||
}
|
||||
}
|
||||
|
||||
|
14
packages/backend/src/types/app-info.ts
Normal file
14
packages/backend/src/types/app-info.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
import FieldType from './field';
|
||||
import AuthenticationStepType from './authentication-step';
|
||||
|
||||
type AppInfo = {
|
||||
name: string,
|
||||
key: string,
|
||||
iconUrl: string,
|
||||
docUrl: string,
|
||||
primaryColor: string,
|
||||
fields: FieldType[],
|
||||
authenticationSteps?: AuthenticationStepType[]
|
||||
}
|
||||
|
||||
export default AppInfo;
|
10
packages/backend/src/types/authentication-step-field.ts
Normal file
10
packages/backend/src/types/authentication-step-field.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
type AuthenticationStepField = {
|
||||
name: string,
|
||||
value: string | null,
|
||||
fields?: {
|
||||
name: string,
|
||||
value: string | null
|
||||
}[]
|
||||
}
|
||||
|
||||
export default AuthenticationStepField;
|
10
packages/backend/src/types/authentication-step.ts
Normal file
10
packages/backend/src/types/authentication-step.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
import AuthenticationStepField from '../types/authentication-step-field';
|
||||
|
||||
type AuthenticationStep = {
|
||||
step: number,
|
||||
type: string,
|
||||
name: string,
|
||||
fields: AuthenticationStepField[];
|
||||
}
|
||||
|
||||
export default AuthenticationStep;
|
2
packages/backend/src/types/field.d.ts
vendored
2
packages/backend/src/types/field.d.ts
vendored
@@ -5,7 +5,7 @@ type Field = {
|
||||
required: boolean,
|
||||
readOnly: boolean,
|
||||
value: string,
|
||||
placeholder: string,
|
||||
placeholder: string | null,
|
||||
description: string,
|
||||
docUrl: string,
|
||||
clickToCopy: boolean
|
||||
|
@@ -7,7 +7,8 @@ type AppIconProps = {
|
||||
};
|
||||
|
||||
export default function AppIcon(props: AppIconProps) {
|
||||
const { color, name, url } = props;
|
||||
const { name, url } = props;
|
||||
const color = url ? 'white' : props.color
|
||||
|
||||
return (
|
||||
<Avatar component="span" variant="square" sx={{ bgcolor: `#${color}` }} src={url} alt={name} />
|
||||
|
Reference in New Issue
Block a user