Skip to content

Commit 070995d

Browse files
committed
Show manual-only services with platform notice in setup wizard
- Services without Docker images show as dashed-border cards with platform notice (e.g. "Windows/Macos/Linux only — earnings tracking available") instead of being hidden - Broken/dead services still filtered out completely - Step 3 shows signup link + tracking notice instead of deploy button for manual-only services - Remove read_only from compose files (breaks Docker socket access)
1 parent ce346c9 commit 070995d

5 files changed

Lines changed: 56 additions & 15 deletions

File tree

app/main.py

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -470,23 +470,22 @@ async def api_services_deployed(request: Request) -> list[dict[str, Any]]:
470470

471471
@app.get("/api/services/available")
472472
async def api_services_available(request: Request) -> list[dict[str, Any]]:
473-
"""Return deployable services from catalog, enriched with deployment status."""
473+
"""Return available services from catalog, enriched with deployment status."""
474474
_require_auth_api(request)
475475
services = catalog.get_services()
476476
deployments = await database.get_deployments()
477477
deployed_slugs = {d["slug"] for d in deployments}
478478

479-
# Only return services that can actually be deployed via Docker
480-
deployable = []
479+
available = []
481480
for svc in services:
482-
docker_conf = svc.get("docker", {})
483-
if not docker_conf or not docker_conf.get("image"):
484-
continue # No Docker image — can't deploy
485481
if svc.get("status") in ("broken", "dead"):
486-
continue # Known non-functional
482+
continue # Known non-functional — hide completely
483+
docker_conf = svc.get("docker", {})
484+
has_image = bool(docker_conf and docker_conf.get("image"))
487485
svc["deployed"] = svc.get("slug", "") in deployed_slugs
488-
deployable.append(svc)
489-
return deployable
486+
svc["manual_only"] = not has_image
487+
available.append(svc)
488+
return available
490489

491490

492491
@app.get("/api/services/{slug}")

app/static/css/style.css

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -473,6 +473,16 @@ img { max-width: 100%; }
473473
.service-card.deployed {
474474
border-color: var(--success);
475475
}
476+
.service-card.manual-only {
477+
opacity: 0.75;
478+
border-style: dashed;
479+
}
480+
.manual-notice {
481+
font-size: 0.75rem;
482+
color: var(--warning, #f59e0b);
483+
margin-top: 6px;
484+
font-style: italic;
485+
}
476486
.deployed-badge {
477487
display: inline-flex;
478488
align-items: center;

app/static/js/app.js

Lines changed: 38 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -715,12 +715,14 @@ const CP = (() => {
715715
function renderWizardServiceCard(svc) {
716716
const isSelected = wizardState.selectedServices.includes(svc.slug);
717717
const isDeployed = svc.deployed;
718+
const isManual = svc.manual_only;
718719
const workerNodes = _wizardWorkerSlugs[svc.slug] || 0;
719720
const totalNodes = (isDeployed ? 1 : 0) + workerNodes;
720721

721722
const classes = ['service-card'];
722723
if (isSelected) classes.push('selected');
723724
if (isDeployed) classes.push('deployed');
725+
if (isManual) classes.push('manual-only');
724726

725727
const earning = svc.earnings
726728
? `$${svc.earnings.monthly_low}-$${svc.earnings.monthly_high}/${svc.earnings.per || 'mo'}`
@@ -732,6 +734,13 @@ const CP = (() => {
732734
deployedBadge = `<span class="deployed-badge"><svg width="10" height="10" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3"><polyline points="20 6 9 17 4 12"/></svg> ${label}</span>`;
733735
}
734736

737+
// Platform notice for manual-only services
738+
let manualNotice = '';
739+
if (isManual) {
740+
const platforms = (svc.platforms || []).map(p => p.charAt(0).toUpperCase() + p.slice(1)).join('/');
741+
manualNotice = `<div class="manual-notice">${platforms || 'Desktop'} only — earnings tracking available</div>`;
742+
}
743+
735744
return `
736745
<div class="${classes.join(' ')}" data-slug="${svc.slug}" onclick="CP.toggleWizardService('${svc.slug}', this)">
737746
<div class="service-card-header">
@@ -742,6 +751,7 @@ const CP = (() => {
742751
</div>
743752
</div>
744753
<div class="service-desc">${escapeHtml(svc.short_description || '')}</div>
754+
${manualNotice}
745755
<div class="service-meta" style="margin-top: 8px;">
746756
<span class="badge badge-available">${earning}</span>
747757
${svc.requirements && svc.requirements.residential_ip ? '<span class="badge badge-residential">Residential IP</span>' : ''}
@@ -777,6 +787,34 @@ const CP = (() => {
777787
}
778788

779789
function renderServiceSetupForm(svc) {
790+
const signupUrl = svc.referral && svc.referral.signup_url
791+
? svc.referral.signup_url.replace('{code}', svc.referral.code || '')
792+
: svc.website || '#';
793+
794+
// Manual-only services: show signup link + earnings tracking notice
795+
if (svc.manual_only) {
796+
const platforms = (svc.platforms || []).map(p => p.charAt(0).toUpperCase() + p.slice(1)).join(', ');
797+
return `
798+
<div class="card" style="margin-bottom: 16px;" id="setup-${svc.slug}">
799+
<div class="card-header">
800+
<h3 class="section-title">${escapeHtml(svc.name)}</h3>
801+
<span class="badge badge-category">${escapeHtml(svc.category)}</span>
802+
</div>
803+
<div style="padding: 8px 0;">
804+
<p style="color: var(--warning, #f59e0b); margin-bottom: 12px;">
805+
<strong>${platforms || 'Desktop'} only</strong> — no Docker image available for automated deployment.
806+
</p>
807+
<p style="color: var(--text-secondary); margin-bottom: 16px;">
808+
Install the app on your device, then CashPilot will track your earnings automatically.
809+
</p>
810+
<a href="${escapeHtml(signupUrl)}" target="_blank" rel="noopener" class="btn btn-primary btn-sm">
811+
Sign Up for ${escapeHtml(svc.name)}
812+
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M18 13v6a2 2 0 01-2 2H5a2 2 0 01-2-2V8a2 2 0 012-2h6"/><polyline points="15 3 21 3 21 9"/><line x1="10" y1="14" x2="21" y2="3"/></svg>
813+
</a>
814+
</div>
815+
</div>`;
816+
}
817+
780818
const envFields = (svc.docker && svc.docker.env || []).map(env => {
781819
const inputType = env.secret ? 'password' : 'text';
782820
return `
@@ -791,10 +829,6 @@ const CP = (() => {
791829
</div>`;
792830
}).join('');
793831

794-
const signupUrl = svc.referral && svc.referral.signup_url
795-
? svc.referral.signup_url.replace('{code}', svc.referral.code || '')
796-
: svc.website || '#';
797-
798832
return `
799833
<div class="card" style="margin-bottom: 16px;" id="setup-${svc.slug}">
800834
<div class="card-header">

docker-compose.fleet.yml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,6 @@ services:
2727
restart: unless-stopped
2828
security_opt:
2929
- no-new-privileges:true
30-
read_only: true
3130
tmpfs:
3231
- /tmp:size=64M
3332

docker-compose.yml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@ services:
2323
restart: unless-stopped
2424
security_opt:
2525
- no-new-privileges:true
26-
read_only: true
2726
tmpfs:
2827
- /tmp:size=64M
2928

0 commit comments

Comments
 (0)