Skip to content

Commit

Permalink
feat: Manual proxy configuration & some refactors (#81)
Browse files Browse the repository at this point in the history
Closes #58

Also add test domain fixtures to ease testing (see #63).
  • Loading branch information
YuukanOO committed Oct 14, 2024
1 parent 047202f commit 60db370
Show file tree
Hide file tree
Showing 162 changed files with 7,341 additions and 5,442 deletions.
1 change: 0 additions & 1 deletion .dockerignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
examples
Makefile
Dockerfile
compose.yml
docs
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ jobs:

release:
name: Release
if: github.ref_name == 'main'
if: github.ref_name == 'main' || github.ref_name == 'next'
runs-on: ubuntu-latest
needs: [backend, frontend]
steps:
Expand Down
11 changes: 9 additions & 2 deletions .releaserc
Original file line number Diff line number Diff line change
@@ -1,14 +1,21 @@
{
"branches": [
"main"
{
"name": "main"
},
{
"name": "next",
"channel": "next",
"prerelease": true
}
],
"plugins": [
"@semantic-release/commit-analyzer",
"@semantic-release/release-notes-generator",
[
"@semantic-release/exec",
{
"prepareCmd": "SEELF_VERSION=${nextRelease.version} make prepare-release && echo \"docker_tags=yuukanoo/seelf:${nextRelease.version},yuukanoo/seelf:latest\" >> \"$GITHUB_ENV\"",
"prepareCmd": "SEELF_VERSION=${nextRelease.version} make prepare-release && sh -c \"if [ ${branch.name} = 'next' ]; then echo 'docker_tags=yuukanoo/seelf:next'; else echo 'docker_tags=yuukanoo/seelf:${nextRelease.version},yuukanoo/seelf:latest'; fi\" >> \"$GITHUB_ENV\"",
"publishCmd": "echo \"docker_publish=true\" >> \"$GITHUB_ENV\""
}
],
Expand Down
4 changes: 2 additions & 2 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,15 @@ RUN npm ci
COPY ./cmd/serve/front .
RUN npm run build

FROM golang:1.21-alpine AS builder
FROM golang:1.23-alpine AS builder
# build-base needed to compile the sqlite3 dependency
RUN apk add --update-cache build-base
WORKDIR /app
COPY go.* ./
RUN go mod download
COPY . .
COPY --from=front_builder /app/build ./cmd/serve/front/build
RUN go build -ldflags="-s -w" -o seelf
RUN make build-back

FROM alpine:3.16
LABEL org.opencontainers.image.authors="[email protected]" \
Expand Down
14 changes: 11 additions & 3 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -7,20 +7,28 @@ serve-docs: # Launch the docs dev server
serve-back: # Launch the backend API and creates an admin user if needed
[email protected] ADMIN_PASSWORD=admin LOG_LEVEL=debug go run main.go serve

test: # Launch every tests
test-front: # Launch the frontend tests
cd cmd/serve/front && npm i && npm test && cd ../../..

test-back: # Launch the backend tests
go vet ./...
go test ./... --cover

test: test-front test-back # Launch every tests

ts: # Print the current timestamp, useful for migrations
@date +%s

outdated: # Print direct dependencies and their latest version
go list -v -u -m -f '{{if not .Indirect}}{{.}}{{end}}' all

build: # Build the final binary for the current platform
build-front: # Build the frontend
cd cmd/serve/front && npm i && npm run build && cd ../../..
go build -ldflags="-s -w" -o seelf

build-back: # Build the backend
go build -tags release -ldflags="-s -w" -o seelf

build: build-front build-back # Build the final binary for the current platform

build-docs: # Build the docs
npm i && npm run docs:build
Expand Down
9 changes: 8 additions & 1 deletion cmd/config/configuration.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import (
"github.com/YuukanOO/seelf/pkg/log"
"github.com/YuukanOO/seelf/pkg/monad"
"github.com/YuukanOO/seelf/pkg/must"
"github.com/YuukanOO/seelf/pkg/ostools"
"github.com/YuukanOO/seelf/pkg/validate"
"github.com/YuukanOO/seelf/pkg/validate/numbers"
)
Expand Down Expand Up @@ -43,7 +44,7 @@ const (
type (
// Configuration used to configure seelf commands.
Configuration interface {
serve.Options // The configuration should provide every settings needed by the seelf server
serve.Options

Initialize(log.ConfigurableLogger, string) error // Initialize the configuration by loading it (from config file, env vars, etc.)
}
Expand Down Expand Up @@ -139,6 +140,11 @@ func (c *configuration) Initialize(logger log.ConfigurableLogger, path string) e
return err
}

// Make sure the data path exists
if err = ostools.MkdirAll(c.Data.Path); err != nil {
return err
}

// Update logger based on loaded configuration
if err = logger.Configure(c.logFormat, c.logLevel); err != nil {
return err
Expand Down Expand Up @@ -173,6 +179,7 @@ func (c *configuration) Secret() []byte { return []by
func (c *configuration) RunnersPollInterval() time.Duration { return c.pollInterval }
func (c *configuration) RunnersDeploymentCount() int { return c.Runners.Deployment }
func (c *configuration) RunnersCleanupCount() int { return c.Runners.Cleanup }
func (c *configuration) IsDebug() bool { return c.logLevel == log.DebugLevel }

func (c *configuration) IsSecure() bool {
// If secure has been explicitly isSet, returns it
Expand Down
3 changes: 2 additions & 1 deletion cmd/serve/front/src/components/target-card.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import Stack from '$components/stack.svelte';
import CleanupNotice from '$components/cleanup-notice.svelte';
import routes from '$lib/path';
import l from '$lib/localization';
import { type Target, TargetStatus } from '$lib/resources/targets';
export let data: Target;
Expand All @@ -27,7 +28,7 @@
<Stack direction="column">
<div>
<h2 class="title"><Link href={routes.editTarget(data.id)}>{data.name}</Link></h2>
<div class="url">{data.url}</div>
<div class="url">{data.url ?? l.translate('target.manual_proxy')}</div>
</div>
<CleanupNotice requested_at={data.cleanup_requested_at} />
</Stack>
Expand Down
10 changes: 6 additions & 4 deletions cmd/serve/front/src/lib/localization/en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,7 @@ const translations = {
'auth.signin.description': 'Please fill the form below to access your dashboard.',
// App
'app.no_targets': 'No targets found',
'app.no_targets.description':
'You need at least one target to deploy your application. Head to the <a href="/targets">create target</a> page to create one.',
'app.no_targets.description': `You need at least one target to deploy your application. Head to the <a href="${routes.createTarget}">create target</a> page to create one.`,
'app.not_found': "Looks like the application you're looking for does not exist. Head back to the",
'app.not_found.cta': 'homepage',
'app.blankslate': `Looks like you have no application yet. <br />Applications represents <strong>services you want to deploy</strong> on your infrastructure. Start by <a href="${routes.createApp}">creating one!</a>`,
Expand Down Expand Up @@ -43,8 +42,8 @@ This action is IRREVERSIBLE and will DELETE ALL DATA associated with this applic
'app.environment.staging': 'Staging settings',
'app.environment.target': 'Deploy target',
'app.environment.target.changed': 'Target changed',
'app.environment.target.changed.description': (url: string) =>
`If you change the target, resources related to this application deployed by seelf on <strong>${url}</strong> will be <strong>REMOVED</strong> and a new deployment on the new target will be queued if possible. If you want to backup something, do it before updating the target.`,
'app.environment.target.changed.description': (name: string) =>
`If you change the target, resources related to this application deployed by seelf on <strong>${name}</strong> will be <strong>REMOVED</strong> and a new deployment on the new target will be queued if possible. If you want to backup something, do it before updating the target.`,
'app.environment.vars': 'Environment variables',
'app.environment.vars.service.add': 'Add service variables',
'app.environment.vars.service.delete': 'Remove service variables',
Expand Down Expand Up @@ -77,6 +76,9 @@ This action is IRREVERSIBLE and will DELETE ALL DATA associated with this applic
'target.blankslate': `Looks like you have no target yet. <br />Targets determine on which host your <strong>applications will be deployed</strong> and which <strong>provider</strong> should be used. Start by <a href="${routes.createTarget}">creating one!</a>`,
'target.general': 'General settings',
'target.name.help': 'The name is being used only for display, it can be anything you want.',
'target.manual_proxy': 'Manual proxy',
'target.automatic_proxy_configuration': 'Expose services automatically',
'target.automatic_proxy_configuration.help': `If enabled, a proxy will be deployed on the target and your services will be <a target="_blank" href="https://yuukanoo.github.io/seelf/reference/providers/docker.html#exposing-services">automatically exposed</a>. If disabled, you will <strong>have to manually expose your services</strong> with your preferred solution. Updating this setting <strong>may</strong> require you to redeploy your applications.`,
'target.url.help':
'All applications deployed on this target will be available as a <strong>subdomain</strong> on this root URL (without path). It should be <strong>unique</strong> among targets. You <strong>MUST</strong> configure a <strong>wildcard DNS</strong> for subdomains such as <code>*.&lt;url above&gt;</code> redirects to this target IP.',
'target.provider': 'Provider',
Expand Down
9 changes: 6 additions & 3 deletions cmd/serve/front/src/lib/localization/fr.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ export default {
'Remplissez le formulaire ci-dessous pour accéder au tableau de bord.',
// App
'app.no_targets': 'Aucune cible trouvée',
'app.no_targets.description': `Vous avez besoin d'au moins une cible pour pouvoir déployer votre application. Dirigez-vous vers la <a href="/targets/new">page de création</a> pour en créer une.`,
'app.no_targets.description': `Vous avez besoin d'au moins une cible pour pouvoir déployer votre application. Dirigez-vous vers la <a href="${routes.createTarget}">page de création</a> pour en créer une.`,
'app.not_found':
"Il semblerait que l'application que vous recherchez n'existe pas. Retournez à la",
'app.not_found.cta': "page d'accueil",
Expand Down Expand Up @@ -47,8 +47,8 @@ Cette action est IRRÉVERSIBLE et supprimera TOUTES LES DONNÉES associées sur
'app.environment.staging': 'Paramètres de staging',
'app.environment.target': 'Cible de déploiement',
'app.environment.target.changed': 'Cible mise à jour',
'app.environment.target.changed.description': (url: string) =>
`Si vous changez de cible, toutes les ressources liées à cette application déployées par seelf sur <strong>${url}</strong> seront <strong>SUPPRIMÉES</strong> et un déploiement sur la nouvelle cible sera programmé si possible. Si vous devez sauvegarder quelque chose, faites le avant de changer la cible.`,
'app.environment.target.changed.description': (name: string) =>
`Si vous changez de cible, toutes les ressources liées à cette application déployées par seelf sur <strong>${name}</strong> seront <strong>SUPPRIMÉES</strong> et un déploiement sur la nouvelle cible sera programmé si possible. Si vous devez sauvegarder quelque chose, faites le avant de changer la cible.`,
'app.environment.vars': "Variables d'environnement",
'app.environment.vars.service.add': 'Ajouter un service',
'app.environment.vars.service.delete': 'Supprimer le service',
Expand Down Expand Up @@ -83,6 +83,9 @@ Cette action est IRRÉVERSIBLE et supprimera TOUTES LES DONNÉES associées sur
'target.blankslate': `Aucune cible pour le moment. <br />Les cibles déterminent sur quel hôte vos <strong>applications seront déployées</strong>. Commencez par <a href="${routes.createTarget}">en créer une !</a>`,
'target.general': 'Paramètres généraux',
'target.name.help': `Le nom est utilisé uniquement pour l'affichage. Vous pouvez choisir ce que vous voulez.`,
'target.manual_proxy': 'Proxy manuel',
'target.automatic_proxy_configuration': 'Exposer les services automatiquement',
'target.automatic_proxy_configuration.help': `Si activé, un proxy sera déployé sur la cible et vos services seront <a target="_blank" href="https://yuukanoo.github.io/seelf/reference/providers/docker.html#exposing-services">automatiquement exposés</a>. Si désactivé, vous <strong>devrez faire le nécessaire</strong> pour rendre vos services accessibles en utilisant la méthode de votre choix. Changer ce paramètre <strong>pourra</strong> nécessiter le redéploiement de vos applications.`,
'target.url.help': `Toutes les applications déployées sur cette cible seront disponibles en tant que <strong>sous-domaine</strong> de cette URL racine (sans sous-chemin). Elle doit être <strong>unique</strong> parmi les cibles. Vous <strong>DEVEZ</strong> configurer un <strong>DNS wildcard</strong> pour les sous-domaines de telle sorte que <code>*.&lt;url configurée&gt;</code> redirige vers l'IP de cette cible.`,
'target.provider': 'Fournisseur',
'target.provider.docker.help': "Docker engine <strong>DOIT</strong> être installé sur l'hôte.",
Expand Down
2 changes: 1 addition & 1 deletion cmd/serve/front/src/lib/resources/apps.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ export type VersionControl = { url: string; token?: string };
export type TargetSummary = {
id: string;
name: string;
url: string;
url?: string;
};

export type AppDetail = {
Expand Down
6 changes: 3 additions & 3 deletions cmd/serve/front/src/lib/resources/targets.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ export type ProviderTypes = ProviderConfigData['kind'];
export type Target = {
id: string;
name: string;
url: string;
url?: string;
provider: ProviderConfigData;
state: TargetState;
cleanup_requested_at?: string;
Expand All @@ -39,7 +39,7 @@ export type Target = {

export type CreateTarget = {
name: string;
url: string;
url?: string;
docker?: {
host?: string;
user?: string;
Expand All @@ -50,7 +50,7 @@ export type CreateTarget = {

export type UpdateTarget = {
name?: string;
url?: string;
url: Patch<string>;
docker?: {
host?: string;
user?: string;
Expand Down
47 changes: 34 additions & 13 deletions cmd/serve/front/src/routes/(main)/apps/app-form.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
import type { Target } from '$lib/resources/targets';
import l from '$lib/localization';
import Dropdown, { type DropdownOption } from '$components/dropdown.svelte';
import ServiceUrl from './service-url.svelte';
export let handler: (data: any) => Promise<unknown>;
export let targets: Target[];
Expand All @@ -40,8 +41,8 @@
let prodScheme = l.translate('app.how.placeholder.scheme');
let prodUrl = l.translate('app.how.placeholder.url');
let stagingScheme = l.translate('app.how.placeholder.scheme');
let stagingUrl = l.translate('app.how.placeholder.url');
let stagingScheme = prodScheme;
let stagingUrl = prodUrl;
const targetsMap = targets.reduce<Record<string, Target>>((acc, value) => {
acc[value.id] = value;
Expand All @@ -51,19 +52,23 @@
$: appName = name || l.translate('app.how.placeholder.name');
$: {
try {
const u = new URL(targetsMap[production.target]?.url);
const u = new URL(targetsMap[production.target]?.url!);
prodScheme = u.protocol + '//';
prodUrl = u.hostname;
} catch {}
} catch {
prodScheme = prodUrl = '';
}
}
$: {
try {
const u = new URL(targetsMap[staging.target]?.url);
const u = new URL(targetsMap[staging.target]?.url!);
stagingScheme = u.protocol + '//';
stagingUrl = u.hostname;
} catch {}
} catch {
stagingScheme = stagingUrl = '';
}
}
const environmentText = l.translate('app.how.env');
Expand All @@ -73,7 +78,7 @@
const targetsOptions = targets.map((target) => ({
value: target.id,
label: `${target.url} - ${target.name}`
label: `${target.url ?? l.translate('target.manual_proxy')} - ${target.name}`
})) satisfies DropdownOption<string>[];
// Type $$Props to narrow the handler function based on wether this is an update or a new app
Expand Down Expand Up @@ -167,19 +172,35 @@
<tr>
<td data-label={environmentText}><strong>production</strong></td>
<td data-label={defaultServiceText}>
{prodScheme}{appName}.{prodUrl}
<ServiceUrl scheme={prodScheme} host={prodUrl} {appName} />
</td>
<td data-label={otherServicesTitleText}>
{prodScheme}dashboard.{appName}.{prodUrl}
<ServiceUrl
scheme={prodScheme}
host={prodUrl}
{appName}
prefix="dashboard."
/>
</td>
</tr>
<tr>
<td data-label={environmentText}><strong>staging</strong></td>
<td data-label={defaultServiceText}>
{stagingScheme}{appName}-staging.{stagingUrl}
<ServiceUrl
scheme={stagingScheme}
host={stagingUrl}
{appName}
suffix="-staging"
/>
</td>
<td data-label={otherServicesTitleText}>
{stagingScheme}dashboard.{appName}-staging.{stagingUrl}
<ServiceUrl
scheme={stagingScheme}
host={stagingUrl}
{appName}
prefix="dashboard."
suffix="-staging"
/>
</td>
</tr>
</tbody>
Expand Down Expand Up @@ -223,7 +244,7 @@
<Panel title="app.environment.target.changed" variant="warning">
<p>
{@html l.translate('app.environment.target.changed.description', [
initialData.production.target.url
initialData.production.target.name
])}
</p>
</Panel>
Expand Down Expand Up @@ -252,7 +273,7 @@
<Panel title="app.environment.target.changed" variant="warning">
<p>
{@html l.translate('app.environment.target.changed.description', [
initialData.production.target.url
initialData.production.target.name
])}
</p>
</Panel>
Expand Down
15 changes: 15 additions & 0 deletions cmd/serve/front/src/routes/(main)/apps/service-url.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<script lang="ts">
import l from '$lib/localization';
export let scheme: string;
export let host: string;
export let appName: string;
export let prefix = '';
export let suffix = '';
</script>

{#if scheme}
{scheme}{prefix}{appName}{suffix}.{host}
{:else}
- ({l.translate('target.manual_proxy')})
{/if}
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
export let data;
const submit = (d: UpdateTarget) =>
service.update(data.target.id, d).then((t) => goto(routes.targets));
service.update(data.target.id, d).then(() => goto(routes.targets));
const {
loading: deleting,
Expand Down
Loading

0 comments on commit 60db370

Please sign in to comment.