Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 8 additions & 5 deletions app/Controllers/Http/ServiceController.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import type { HttpContextContract } from '@ioc:Adonis/Core/HttpContext';
import { schema } from '@ioc:Adonis/Core/Validator';
import Service from 'App/Models/Service';
import { url } from 'Config/app';
import { getAppUrl } from 'Config/app';
import { v4 as uuid } from 'uuid';
import * as fs from 'fs-extra';
import path from 'node:path';
Expand Down Expand Up @@ -90,6 +90,7 @@ export default class ServiceController {

const { id } = user;
const services = await loadUserServices(user.id);
const appUrl = getAppUrl(request.hostname());

// Convert to array with all data Franz wants
// eslint-disable-next-line @typescript-eslint/no-explicit-any
Expand All @@ -101,7 +102,7 @@ export default class ServiceController {
// eslint-disable-next-line unicorn/no-null
let iconUrl: string | null = null;
if (settings.iconId) {
iconUrl = `${url}/v1/icon/${settings.iconId}`;
iconUrl = `${appUrl}/v1/icon/${settings.iconId}`;
}

return {
Expand Down Expand Up @@ -162,6 +163,7 @@ export default class ServiceController {
}

const { id } = params;
const appUrl = getAppUrl(request.hostname());
const service = await Service.query()
.where('serviceId', id)
.where('userId', user.id)
Expand Down Expand Up @@ -224,7 +226,7 @@ export default class ServiceController {
id,
name: service.name,
...newSettings,
iconUrl: `${url}/v1/icon/${newSettings.iconId}`,
iconUrl: `${appUrl}/v1/icon/${newSettings.iconId}`,
userId: user.id,
},
status: ['updated'],
Expand Down Expand Up @@ -272,7 +274,7 @@ export default class ServiceController {
id,
name: serviceUpdated.name,
...settings,
iconUrl: `${url}/v1/icon/${settings.iconId}`,
iconUrl: `${appUrl}/v1/icon/${settings.iconId}`,
userId: user.id,
},
status: ['updated'],
Expand All @@ -289,6 +291,7 @@ export default class ServiceController {
}

const data = request.all();
const appUrl = getAppUrl(request.hostname());

for (const service of Object.keys(data)) {
// Get current settings from db
Expand Down Expand Up @@ -326,7 +329,7 @@ export default class ServiceController {
// eslint-disable-next-line unicorn/no-null
let iconUrl: string | null = null;
if (settings.iconId) {
iconUrl = `${url}/v1/icon/${settings.iconId}`;
iconUrl = `${appUrl}/v1/icon/${settings.iconId}`;
}

return {
Expand Down
2 changes: 1 addition & 1 deletion app/Models/User.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import Token from './Token';
import Workspace from './Workspace';
import Service from './Service';
import Mail from '@ioc:Adonis/Addons/Mail';
import { url } from 'Config/app';
import { url } from 'Config/app'; // Primary URL for emails
import { mailFrom } from 'Config/dashboard';

export default class User extends BaseModel {
Expand Down
44 changes: 43 additions & 1 deletion config/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,49 @@ import { ValidatorConfig } from '@ioc:Adonis/Core/Validator';
*/
export const appKey: string = Env.get('APP_KEY');

export const url: string = Env.get('APP_URL');
// Parse APP_URL(S) - supports both single URL and comma-separated multiple URLs
function parseAppUrls(): string[] {
const appUrls = Env.get('APP_URLS');
const appUrl = Env.get('APP_URL');

if (appUrls) {
return appUrls.split(',').map((u: string) => u.trim()).filter(Boolean);
}

if (appUrl) {
return [appUrl];
}

throw new Error('Either APP_URL or APP_URLS must be defined');
}

export const urls: string[] = parseAppUrls();
export const url: string = urls[0]; // Primary URL for backward compatibility

/**
* Get the appropriate app URL based on the request hostname.
* Falls back to the primary URL if no match or no request provided.
*
* @param requestHostname - Optional hostname from the incoming request
* @returns The matching URL or primary URL
*/
export function getAppUrl(requestHostname?: string): string {
if (!requestHostname) {
return url;
}

// Try to find a URL that matches the request hostname
const matchingUrl = urls.find(u => {
try {
const urlObj = new URL(u);
return urlObj.hostname === requestHostname;
} catch {
return false;
}
});

return matchingUrl || url;
}

// TODO: this is parsed as string to be coherent with the previous version of the code we add (before migrating to AdonisJS 5)
export const isRegistrationEnabled: string = Env.get('IS_REGISTRATION_ENABLED');
Expand Down
2 changes: 2 additions & 0 deletions env.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ export default Env.rules({

APP_KEY: Env.schema.string(),
APP_NAME: Env.schema.string(),
APP_URL: Env.schema.string.optional(),
APP_URLS: Env.schema.string.optional(),

NODE_ENV: Env.schema.enum(['development', 'production', 'test'] as const),
});