mirror of
https://github.com/abusoww/tuxmate.git
synced 2026-04-17 19:53:11 +02:00
fix: resolve CORS issues in 39dbc46 by moving verification to build-time
- Replace runtime Flathub API calls with static JSON generation - Add prebuild and predev scripts to specificially fetch verification data - Refactor useVerification hook to be synchronous - Fixes Flathub API CORS errors in production
This commit is contained in:
@@ -3,6 +3,8 @@
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"prebuild": "node scripts/fetch-verified-flatpaks.mjs",
|
||||
"predev": "node scripts/fetch-verified-flatpaks.mjs",
|
||||
"dev": "next dev",
|
||||
"build": "next build",
|
||||
"start": "next start",
|
||||
|
||||
102
scripts/fetch-verified-flatpaks.mjs
Normal file
102
scripts/fetch-verified-flatpaks.mjs
Normal file
@@ -0,0 +1,102 @@
|
||||
import fs from 'node:fs/promises';
|
||||
import path from 'node:path';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
|
||||
// Build-time fetcher for Flathub verified apps
|
||||
// Baking this into JSON avoids CORS headaches and runtime latency
|
||||
|
||||
const CONFIG = {
|
||||
API_ENDPOINT: 'https://flathub.org/api/v2/search',
|
||||
OUTPUT_FILE: '../src/lib/verified-flatpaks.json',
|
||||
HITS_PER_PAGE: 250, // Flathub's max page size
|
||||
MAX_RETRIES: 3,
|
||||
TIMEOUT_MS: 15_000,
|
||||
};
|
||||
|
||||
// resolvePath helper because ESM __dirname is awkward
|
||||
const resolvePath = (rel) => path.join(path.dirname(fileURLToPath(import.meta.url)), rel);
|
||||
const sleep = (ms) => new Promise(r => setTimeout(r, ms));
|
||||
|
||||
// Robust fetch wrapper - APIs are flaky, we are resilient
|
||||
async function fetchWithRetry(url, options, retries = CONFIG.MAX_RETRIES) {
|
||||
try {
|
||||
const controller = new AbortController();
|
||||
const id = setTimeout(() => controller.abort(), CONFIG.TIMEOUT_MS);
|
||||
|
||||
const response = await fetch(url, { ...options, signal: controller.signal });
|
||||
clearTimeout(id);
|
||||
|
||||
if (!response.ok) throw new Error(`HTTP ${response.status}`);
|
||||
return await response.json();
|
||||
} catch (error) {
|
||||
if (retries > 0) {
|
||||
const delay = 1000 * (CONFIG.MAX_RETRIES - retries + 1);
|
||||
console.warn(`Request failed (${error.message}). Retrying in ${delay}ms...`);
|
||||
await sleep(delay);
|
||||
return fetchWithRetry(url, options, retries - 1);
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
async function fetchPage(page) {
|
||||
const data = await fetchWithRetry(CONFIG.API_ENDPOINT, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
query: '',
|
||||
filter: 'verification_verified=true',
|
||||
page,
|
||||
hitsPerPage: CONFIG.HITS_PER_PAGE,
|
||||
}),
|
||||
});
|
||||
|
||||
return {
|
||||
ids: (data.hits || []).filter(h => h.verification_verified && h.app_id).map(h => h.app_id),
|
||||
totalPages: data.totalPages || 0
|
||||
};
|
||||
}
|
||||
|
||||
async function main() {
|
||||
console.log(`\n📦 Syncing Flathub verified apps...`);
|
||||
const startTime = Date.now();
|
||||
|
||||
try {
|
||||
// Grab page 1 to see what we're dealing with
|
||||
const firstPage = await fetchPage(1);
|
||||
const allApps = new Set(firstPage.ids);
|
||||
|
||||
// Cap at 50 pages because if there are >12,000 verified apps, we have bigger problems
|
||||
const totalPages = Math.min(firstPage.totalPages, 50);
|
||||
|
||||
if (totalPages > 1) {
|
||||
// Blast the remaining pages in parallel
|
||||
// console.log(`Fetching ${totalPages - 1} more pages...`);
|
||||
const pages = Array.from({ length: totalPages - 1 }, (_, i) => i + 2);
|
||||
|
||||
const results = await Promise.all(
|
||||
pages.map(p => fetchPage(p).then(r => r.ids).catch(() => []))
|
||||
);
|
||||
results.flat().forEach(id => allApps.add(id));
|
||||
}
|
||||
|
||||
// Sort for deterministic output (git diffs will thank us)
|
||||
const apps = Array.from(allApps).sort();
|
||||
const outputPath = resolvePath(CONFIG.OUTPUT_FILE);
|
||||
|
||||
await fs.writeFile(outputPath, JSON.stringify({
|
||||
meta: { fetchedAt: new Date().toISOString() },
|
||||
count: apps.length,
|
||||
apps
|
||||
}, null, 2));
|
||||
|
||||
const time = ((Date.now() - startTime) / 1000).toFixed(2);
|
||||
console.log(`✅ Cached ${apps.length} verified apps in ${time}s`);
|
||||
|
||||
} catch (e) {
|
||||
console.error(`💥 Failed to sync verified apps:`, e);
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
main();
|
||||
@@ -1,78 +1,49 @@
|
||||
'use client';
|
||||
|
||||
import { useState, useEffect, useCallback, useRef } from 'react';
|
||||
import { useCallback } from 'react';
|
||||
import type { DistroId } from '@/lib/data';
|
||||
import {
|
||||
fetchFlathubVerifiedApps,
|
||||
isFlathubVerified,
|
||||
isSnapVerified,
|
||||
} from '@/lib/verification';
|
||||
|
||||
export interface UseVerificationResult {
|
||||
// Kept for compatibility, always false now
|
||||
isLoading: boolean;
|
||||
hasError: boolean;
|
||||
isVerified: (distro: DistroId, packageName: string) => boolean;
|
||||
getVerificationSource: (distro: DistroId, packageName: string) => 'flathub' | 'snap' | null;
|
||||
}
|
||||
|
||||
// Fetches Flathub data on mount, Snap uses static list (instant)
|
||||
// Now purely synchronous using build-time generated data
|
||||
export function useVerification(): UseVerificationResult {
|
||||
const [isLoading, setIsLoading] = useState(true);
|
||||
const [hasError, setHasError] = useState(false);
|
||||
const [flathubReady, setFlathubReady] = useState(false);
|
||||
const fetchedRef = useRef(false);
|
||||
|
||||
useEffect(() => {
|
||||
if (fetchedRef.current) return;
|
||||
fetchedRef.current = true;
|
||||
|
||||
let isMounted = true;
|
||||
|
||||
fetchFlathubVerifiedApps()
|
||||
.then(() => {
|
||||
if (isMounted) setFlathubReady(true);
|
||||
})
|
||||
.catch((error) => {
|
||||
if (isMounted) {
|
||||
console.error('Failed to fetch Flathub verification:', error);
|
||||
setHasError(true);
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
if (isMounted) setIsLoading(false);
|
||||
});
|
||||
|
||||
return () => {
|
||||
isMounted = false;
|
||||
};
|
||||
}, []);
|
||||
|
||||
// Check if package is verified for the distro
|
||||
const isVerified = useCallback((distro: DistroId, packageName: string): boolean => {
|
||||
if (distro === 'flatpak' && flathubReady) {
|
||||
if (distro === 'flatpak') {
|
||||
return isFlathubVerified(packageName);
|
||||
}
|
||||
if (distro === 'snap') {
|
||||
return isSnapVerified(packageName);
|
||||
}
|
||||
return false;
|
||||
}, [flathubReady]);
|
||||
}, []);
|
||||
|
||||
// Get verification source for badge styling
|
||||
const getVerificationSource = useCallback((distro: DistroId, packageName: string): 'flathub' | 'snap' | null => {
|
||||
if (distro === 'flatpak' && flathubReady && isFlathubVerified(packageName)) {
|
||||
if (distro === 'flatpak' && isFlathubVerified(packageName)) {
|
||||
return 'flathub';
|
||||
}
|
||||
if (distro === 'snap' && isSnapVerified(packageName)) {
|
||||
return 'snap';
|
||||
}
|
||||
return null;
|
||||
}, [flathubReady]);
|
||||
}, []);
|
||||
|
||||
return {
|
||||
isLoading,
|
||||
hasError,
|
||||
isLoading: false,
|
||||
hasError: false,
|
||||
isVerified,
|
||||
getVerificationSource,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -1,188 +1,21 @@
|
||||
// Flatpak/Snap verification status - shows badges for verified publishers
|
||||
// Data is fetched at build time to avoid CORS issues with Flathub API
|
||||
|
||||
// Flathub API response shape
|
||||
interface FlathubSearchResponse {
|
||||
hits: Array<{
|
||||
app_id: string;
|
||||
verification_verified: boolean;
|
||||
}>;
|
||||
totalPages: number;
|
||||
totalHits: number;
|
||||
}
|
||||
import verifiedFlatpaks from './verified-flatpaks.json';
|
||||
import verifiedSnaps from './verified-snaps.json';
|
||||
|
||||
// Module-level cache
|
||||
let flathubVerifiedCache: Set<string> | null = null;
|
||||
|
||||
// localStorage cache key and TTL (1 hour)
|
||||
const CACHE_KEY = 'tuxmate_verified_flatpaks';
|
||||
const CACHE_TTL_MS = 60 * 60 * 1000;
|
||||
|
||||
// Known verified Snap publishers (static list - Snapcraft API doesn't support CORS)
|
||||
const KNOWN_VERIFIED_SNAP_PACKAGES = new Set([
|
||||
// Mozilla
|
||||
'firefox', 'thunderbird',
|
||||
// Canonical/Ubuntu
|
||||
'chromium',
|
||||
// Brave
|
||||
'brave',
|
||||
// Spotify
|
||||
'spotify',
|
||||
// Microsoft
|
||||
'code',
|
||||
// JetBrains
|
||||
'intellij-idea-community', 'intellij-idea-ultimate', 'pycharm-community', 'pycharm-professional',
|
||||
// Slack
|
||||
'slack',
|
||||
// Discord
|
||||
'discord',
|
||||
// Signal
|
||||
'signal-desktop',
|
||||
// Telegram
|
||||
'telegram-desktop',
|
||||
// Zoom
|
||||
'zoom-client',
|
||||
// Obsidian
|
||||
'obsidian',
|
||||
// Bitwarden
|
||||
'bitwarden',
|
||||
// Creative
|
||||
'blender', 'gimp', 'inkscape', 'krita',
|
||||
// Media
|
||||
'vlc', 'obs-studio',
|
||||
// Office
|
||||
'libreoffice',
|
||||
// Dev
|
||||
'node', 'go', 'rustup', 'ruby', 'cmake', 'docker', 'kubectl',
|
||||
// Gaming
|
||||
'steam', 'retroarch',
|
||||
// Browser
|
||||
'vivaldi',
|
||||
]);
|
||||
|
||||
// Try to load from localStorage cache
|
||||
function loadFromCache(): Set<string> | null {
|
||||
try {
|
||||
const cached = localStorage.getItem(CACHE_KEY);
|
||||
if (!cached) return null;
|
||||
|
||||
const { data, timestamp } = JSON.parse(cached);
|
||||
if (Date.now() - timestamp > CACHE_TTL_MS) {
|
||||
localStorage.removeItem(CACHE_KEY);
|
||||
return null;
|
||||
}
|
||||
|
||||
return new Set(data);
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// Save to localStorage cache
|
||||
function saveToCache(apps: Set<string>): void {
|
||||
try {
|
||||
localStorage.setItem(CACHE_KEY, JSON.stringify({
|
||||
data: Array.from(apps),
|
||||
timestamp: Date.now(),
|
||||
}));
|
||||
} catch {
|
||||
// localStorage might be full or disabled
|
||||
}
|
||||
}
|
||||
|
||||
// Fetch a single page
|
||||
async function fetchPage(page: number): Promise<string[]> {
|
||||
const response = await fetch('https://flathub.org/api/v2/search', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
query: '',
|
||||
filter: 'verification_verified=true',
|
||||
page,
|
||||
hitsPerPage: 250,
|
||||
}),
|
||||
signal: AbortSignal.timeout(15000),
|
||||
});
|
||||
|
||||
if (!response.ok) return [];
|
||||
|
||||
const data: FlathubSearchResponse = await response.json();
|
||||
return data.hits
|
||||
.filter(h => h.verification_verified && h.app_id)
|
||||
.map(h => h.app_id);
|
||||
}
|
||||
|
||||
// Fetch all verified Flatpak app IDs (parallel + cached)
|
||||
export async function fetchFlathubVerifiedApps(): Promise<Set<string>> {
|
||||
// Return memory cache if available
|
||||
if (flathubVerifiedCache !== null) {
|
||||
return flathubVerifiedCache;
|
||||
}
|
||||
|
||||
// Try localStorage cache
|
||||
const cached = loadFromCache();
|
||||
if (cached) {
|
||||
flathubVerifiedCache = cached;
|
||||
return cached;
|
||||
}
|
||||
|
||||
// Fetch page 1 to get totalPages
|
||||
try {
|
||||
const firstResponse = await fetch('https://flathub.org/api/v2/search', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
query: '',
|
||||
filter: 'verification_verified=true',
|
||||
page: 1,
|
||||
hitsPerPage: 250,
|
||||
}),
|
||||
signal: AbortSignal.timeout(15000),
|
||||
});
|
||||
|
||||
if (!firstResponse.ok) {
|
||||
console.warn('Flathub API returned', firstResponse.status);
|
||||
flathubVerifiedCache = new Set();
|
||||
return flathubVerifiedCache;
|
||||
}
|
||||
|
||||
const firstData: FlathubSearchResponse = await firstResponse.json();
|
||||
const verifiedApps = new Set<string>(
|
||||
firstData.hits.filter(h => h.verification_verified && h.app_id).map(h => h.app_id)
|
||||
);
|
||||
|
||||
// Fetch remaining pages in parallel (limit to 20 pages = 5,000 apps)
|
||||
const totalPages = Math.min(firstData.totalPages, 20);
|
||||
if (totalPages > 1) {
|
||||
const pagePromises = [];
|
||||
for (let p = 2; p <= totalPages; p++) {
|
||||
pagePromises.push(fetchPage(p));
|
||||
}
|
||||
|
||||
const results = await Promise.all(pagePromises);
|
||||
for (const appIds of results) {
|
||||
for (const id of appIds) {
|
||||
verifiedApps.add(id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
flathubVerifiedCache = verifiedApps;
|
||||
saveToCache(verifiedApps);
|
||||
return verifiedApps;
|
||||
} catch (error) {
|
||||
console.warn('Failed to fetch Flathub verification data:', error);
|
||||
flathubVerifiedCache = new Set();
|
||||
return flathubVerifiedCache;
|
||||
}
|
||||
}
|
||||
// Load static lists (fetched at build time)
|
||||
const VERIFIED_FLATPAK_APPS = new Set(verifiedFlatpaks.apps);
|
||||
const VERIFIED_SNAP_PACKAGES = new Set(verifiedSnaps.apps);
|
||||
|
||||
// Check if a Flatpak app ID is verified
|
||||
export function isFlathubVerified(appId: string): boolean {
|
||||
return flathubVerifiedCache?.has(appId) ?? false;
|
||||
return VERIFIED_FLATPAK_APPS.has(appId);
|
||||
}
|
||||
|
||||
// Check if a Snap package is from a verified publisher
|
||||
export function isSnapVerified(snapName: string): boolean {
|
||||
const cleanName = snapName.split(' ')[0];
|
||||
return KNOWN_VERIFIED_SNAP_PACKAGES.has(cleanName);
|
||||
return VERIFIED_SNAP_PACKAGES.has(cleanName);
|
||||
}
|
||||
|
||||
|
||||
309
src/lib/verified-flatpaks.json
Normal file
309
src/lib/verified-flatpaks.json
Normal file
@@ -0,0 +1,309 @@
|
||||
{
|
||||
"fetchedAt": "2026-01-25T17:01:05.599Z",
|
||||
"count": 303,
|
||||
"apps": [
|
||||
"app.drey.Warp",
|
||||
"app.organicmaps.desktop",
|
||||
"app.polychromatic.controller",
|
||||
"app.twintaillauncher.ttl",
|
||||
"app.xemu.xemu",
|
||||
"app.zen_browser.zen",
|
||||
"be.alexandervanhee.gradia",
|
||||
"best.ellie.StartupConfiguration",
|
||||
"ca.parallel_launcher.ParallelLauncher",
|
||||
"ch.tlaun.TL",
|
||||
"com.actualbudget.actual",
|
||||
"com.adamcake.Bolt",
|
||||
"com.atlauncher.ATLauncher",
|
||||
"com.bambulab.BambuStudio",
|
||||
"com.bitwarden.desktop",
|
||||
"com.bitwig.BitwigStudio",
|
||||
"com.brave.Browser",
|
||||
"com.collaboraoffice.Office",
|
||||
"com.dec05eba.gpu_screen_recorder",
|
||||
"com.discordapp.Discord",
|
||||
"com.freerdp.FreeRDP",
|
||||
"com.geeks3d.furmark",
|
||||
"com.github.IsmaelMartinez.teams_for_linux",
|
||||
"com.github.KRTirtho.Spotube",
|
||||
"com.github.Matoking.protontricks",
|
||||
"com.github.PintaProject.Pinta",
|
||||
"com.github.Rosalie241.RMG",
|
||||
"com.github.d4nj1.tlpui",
|
||||
"com.github.dail8859.NotepadNext",
|
||||
"com.github.finefindus.eyedropper",
|
||||
"com.github.flxzt.rnote",
|
||||
"com.github.hluk.copyq",
|
||||
"com.github.iwalton3.jellyfin-media-player",
|
||||
"com.github.jeromerobert.pdfarranger",
|
||||
"com.github.jkotra.eovpn",
|
||||
"com.github.johnfactotum.Foliate",
|
||||
"com.github.joseexposito.touche",
|
||||
"com.github.louis77.tuner",
|
||||
"com.github.maoschanz.drawing",
|
||||
"com.github.mtkennerly.ludusavi",
|
||||
"com.github.neithern.g4music",
|
||||
"com.github.phase1geo.minder",
|
||||
"com.github.qarmin.czkawka",
|
||||
"com.github.rafostar.Clapper",
|
||||
"com.github.sdv43.whaler",
|
||||
"com.github.taiko2k.tauonmb",
|
||||
"com.github.tchx84.Flatseal",
|
||||
"com.github.tenderowl.frog",
|
||||
"com.github.unrud.VideoDownloader",
|
||||
"com.github.wwmm.easyeffects",
|
||||
"com.github.wwmm.pulseeffects",
|
||||
"com.github.xournalpp.xournalpp",
|
||||
"com.github.zocker_160.SyncThingy",
|
||||
"com.github.ztefn.haguichi",
|
||||
"com.heroicgameslauncher.hgl",
|
||||
"com.jeffser.Alpaca",
|
||||
"com.ktechpit.orion",
|
||||
"com.ktechpit.ultimate-media-downloader",
|
||||
"com.ktechpit.whatsie",
|
||||
"com.logseq.Logseq",
|
||||
"com.lunarclient.LunarClient",
|
||||
"com.markopejic.downloader",
|
||||
"com.mastermindzh.tidal-hifi",
|
||||
"com.mattjakeman.ExtensionManager",
|
||||
"com.ml4w.dotfilesinstaller",
|
||||
"com.modrinth.ModrinthApp",
|
||||
"com.moonlight_stream.Moonlight",
|
||||
"com.notesnook.Notesnook",
|
||||
"com.obsproject.Studio",
|
||||
"com.play0ad.zeroad",
|
||||
"com.plexamp.Plexamp",
|
||||
"com.pokemmo.PokeMMO",
|
||||
"com.prusa3d.PrusaSlicer",
|
||||
"com.rafaelmardojai.Blanket",
|
||||
"com.ranfdev.DistroShelf",
|
||||
"com.rcloneui.RcloneUI",
|
||||
"com.rtosta.zapzap",
|
||||
"com.rustdesk.RustDesk",
|
||||
"com.stremio.Stremio",
|
||||
"com.surfshark.Surfshark",
|
||||
"com.thincast.client",
|
||||
"com.tomjwatson.Emote",
|
||||
"com.usebottles.bottles",
|
||||
"com.usebruno.Bruno",
|
||||
"com.valvesoftware.SteamLink",
|
||||
"com.vixalien.sticky",
|
||||
"com.vscodium.codium",
|
||||
"com.vysp3r.ProtonPlus",
|
||||
"com.warlordsoftwares.media-downloader",
|
||||
"com.warlordsoftwares.tube2go",
|
||||
"com.warlordsoftwares.youtube-downloader-4ktube",
|
||||
"de.haeckerfelix.Fragments",
|
||||
"de.haeckerfelix.Shortwave",
|
||||
"dev.bragefuglseth.Keypunch",
|
||||
"dev.deedles.Trayscale",
|
||||
"dev.edfloreshz.CosmicTweaks",
|
||||
"dev.fredol.open-tv",
|
||||
"dev.goats.xivlauncher",
|
||||
"dev.lizardbyte.app.Sunshine",
|
||||
"dev.qwery.AddWater",
|
||||
"dev.vencord.Vesktop",
|
||||
"eu.betterbird.Betterbird",
|
||||
"fr.handbrake.ghb",
|
||||
"hu.irl.cameractrls",
|
||||
"info.cemu.Cemu",
|
||||
"info.febvre.Komikku",
|
||||
"info.smplayer.SMPlayer",
|
||||
"io.bassi.Amberol",
|
||||
"io.freetubeapp.FreeTube",
|
||||
"io.gdevelop.ide",
|
||||
"io.github.Faugus.faugus-launcher",
|
||||
"io.github.Foldex.AdwSteamGtk",
|
||||
"io.github.IshuSinghSE.aurynk",
|
||||
"io.github.JakubMelka.Pdf4qt",
|
||||
"io.github.Soundux",
|
||||
"io.github.aandrew_me.ytdn",
|
||||
"io.github.alainm23.planify",
|
||||
"io.github.amit9838.mousam",
|
||||
"io.github.antimicrox.antimicrox",
|
||||
"io.github.arunsivaramanneo.GPUViewer",
|
||||
"io.github.astralvixen.geforce-infinity",
|
||||
"io.github.benjamimgois.goverlay",
|
||||
"io.github.celluloid_player.Celluloid",
|
||||
"io.github.cosmic_utils.camera",
|
||||
"io.github.debasish_patra_1987.linuxthemestore",
|
||||
"io.github.dvlv.boxbuddyrs",
|
||||
"io.github.fastrizwaan.WineCharm",
|
||||
"io.github.fastrizwaan.WineZGUI",
|
||||
"io.github.flattool.Ignition",
|
||||
"io.github.flattool.Warehouse",
|
||||
"io.github.getnf.embellish",
|
||||
"io.github.giantpinkrobots.bootqt",
|
||||
"io.github.giantpinkrobots.flatsweep",
|
||||
"io.github.giantpinkrobots.varia",
|
||||
"io.github.ilya_zlobintsev.LACT",
|
||||
"io.github.jeffshee.Hidamari",
|
||||
"io.github.jonmagon.kdiskmark",
|
||||
"io.github.kolunmi.Bazaar",
|
||||
"io.github.linx_systems.ClamUI",
|
||||
"io.github.mhogomchungu.media-downloader",
|
||||
"io.github.nozwock.Packet",
|
||||
"io.github.peazip.PeaZip",
|
||||
"io.github.prateekmedia.appimagepool",
|
||||
"io.github.qwersyk.Newelle",
|
||||
"io.github.radiolamp.mangojuice",
|
||||
"io.github.realmazharhussain.GdmSettings",
|
||||
"io.github.revisto.drum-machine",
|
||||
"io.github.seadve.Kooha",
|
||||
"io.github.shiiion.primehack",
|
||||
"io.github.shonebinu.Brief",
|
||||
"io.github.streetpea.Chiaki4deck",
|
||||
"io.github.thetumultuousunicornofdarkness.cpu-x",
|
||||
"io.github.tobagin.karere",
|
||||
"io.github.ungoogled_software.ungoogled_chromium",
|
||||
"io.github.unknownskl.greenlight",
|
||||
"io.github.wiiznokes.fan-control",
|
||||
"io.github.wivrn.wivrn",
|
||||
"io.github.zarestia_dev.rclone-manager",
|
||||
"io.gitlab.adhami3310.Converter",
|
||||
"io.gitlab.adhami3310.Impression",
|
||||
"io.gitlab.librewolf-community",
|
||||
"io.gitlab.news_flash.NewsFlash",
|
||||
"io.gitlab.theevilskeleton.Upscaler",
|
||||
"io.missioncenter.MissionCenter",
|
||||
"io.podman_desktop.PodmanDesktop",
|
||||
"it.mijorus.gearlever",
|
||||
"jp.nonbili.noutube",
|
||||
"md.obsidian.Obsidian",
|
||||
"me.iepure.devtoolbox",
|
||||
"me.timschneeberger.jdsp4linux",
|
||||
"moe.launcher.an-anime-game-launcher",
|
||||
"net.codelogistics.webapps",
|
||||
"net.davidotek.pupgui2",
|
||||
"net.kuribo64.melonDS",
|
||||
"net.lutris.Lutris",
|
||||
"net.mkiol.SpeechNote",
|
||||
"net.nokyan.Resources",
|
||||
"net.nymtech.NymVPN",
|
||||
"net.pcsx2.PCSX2",
|
||||
"net.retrodeck.retrodeck",
|
||||
"net.shadps4.shadPS4",
|
||||
"net.supertuxkart.SuperTuxKart",
|
||||
"net.waterfox.waterfox",
|
||||
"network.loki.Session",
|
||||
"one.ablaze.floorp",
|
||||
"org.azahar_emu.Azahar",
|
||||
"org.bunkus.mkvtoolnix-gui",
|
||||
"org.cryptomator.Cryptomator",
|
||||
"org.deskflow.deskflow",
|
||||
"org.dupot.easyflatpak",
|
||||
"org.equicord.equibop",
|
||||
"org.fcitx.Fcitx5",
|
||||
"org.fedoraproject.MediaWriter",
|
||||
"org.fkoehler.KTailctl",
|
||||
"org.flameshot.Flameshot",
|
||||
"org.freac.freac",
|
||||
"org.freecad.FreeCAD",
|
||||
"org.freedownloadmanager.Manager",
|
||||
"org.gimp.GIMP",
|
||||
"org.gnome.Boxes",
|
||||
"org.gnome.Calculator",
|
||||
"org.gnome.Calendar",
|
||||
"org.gnome.Characters",
|
||||
"org.gnome.Connections",
|
||||
"org.gnome.Contacts",
|
||||
"org.gnome.Decibels",
|
||||
"org.gnome.DejaDup",
|
||||
"org.gnome.Epiphany",
|
||||
"org.gnome.Evince",
|
||||
"org.gnome.Evolution",
|
||||
"org.gnome.Extensions",
|
||||
"org.gnome.Firmware",
|
||||
"org.gnome.Logs",
|
||||
"org.gnome.Loupe",
|
||||
"org.gnome.Mahjongg",
|
||||
"org.gnome.Maps",
|
||||
"org.gnome.Mines",
|
||||
"org.gnome.Papers",
|
||||
"org.gnome.Shotwell",
|
||||
"org.gnome.Showtime",
|
||||
"org.gnome.SimpleScan",
|
||||
"org.gnome.Snapshot",
|
||||
"org.gnome.TextEditor",
|
||||
"org.gnome.Weather",
|
||||
"org.gnome.World.PikaBackup",
|
||||
"org.gnome.World.Secrets",
|
||||
"org.gnome.baobab",
|
||||
"org.gnome.clocks",
|
||||
"org.gnome.font-viewer",
|
||||
"org.gnome.gitlab.YaLTeR.VideoTrimmer",
|
||||
"org.gnome.gitlab.somas.Apostrophe",
|
||||
"org.inkscape.Inkscape",
|
||||
"org.jellyfin.JellyfinDesktop",
|
||||
"org.jellyfin.JellyfinServer",
|
||||
"org.kde.ark",
|
||||
"org.kde.audiotube",
|
||||
"org.kde.digikam",
|
||||
"org.kde.dolphin",
|
||||
"org.kde.elisa",
|
||||
"org.kde.falkon",
|
||||
"org.kde.filelight",
|
||||
"org.kde.gcompris",
|
||||
"org.kde.gwenview",
|
||||
"org.kde.haruna",
|
||||
"org.kde.isoimagewriter",
|
||||
"org.kde.kamoso",
|
||||
"org.kde.kate",
|
||||
"org.kde.kcalc",
|
||||
"org.kde.kclock",
|
||||
"org.kde.kdenlive",
|
||||
"org.kde.kolourpaint",
|
||||
"org.kde.konsole",
|
||||
"org.kde.kpat",
|
||||
"org.kde.krita",
|
||||
"org.kde.ktorrent",
|
||||
"org.kde.kweather",
|
||||
"org.kde.minuet",
|
||||
"org.kde.okular",
|
||||
"org.keepassxc.KeePassXC",
|
||||
"org.kicad.KiCad",
|
||||
"org.learningequality.Kolibri",
|
||||
"org.librecad.librecad",
|
||||
"org.libreoffice.LibreOffice",
|
||||
"org.libretro.RetroArch",
|
||||
"org.linux_hardware.hw-probe",
|
||||
"org.localsend.localsend_app",
|
||||
"org.luanti.luanti",
|
||||
"org.mixxx.Mixxx",
|
||||
"org.mozilla.Thunderbird",
|
||||
"org.mozilla.firefox",
|
||||
"org.mozilla.vpn",
|
||||
"org.nickvision.tubeconverter",
|
||||
"org.nicotine_plus.Nicotine",
|
||||
"org.onlyoffice.desktopeditors",
|
||||
"org.openrgb.OpenRGB",
|
||||
"org.ppsspp.PPSSPP",
|
||||
"org.prismlauncher.PrismLauncher",
|
||||
"org.pulseaudio.pavucontrol",
|
||||
"org.qbittorrent.qBittorrent",
|
||||
"org.remmina.Remmina",
|
||||
"org.scummvm.ScummVM",
|
||||
"org.shotcut.Shotcut",
|
||||
"org.srb2.SRB2",
|
||||
"org.stellarium.Stellarium",
|
||||
"org.strawberrymusicplayer.strawberry",
|
||||
"org.telegram.desktop",
|
||||
"org.tenacityaudio.Tenacity",
|
||||
"org.torproject.torbrowser-launcher",
|
||||
"org.turbowarp.TurboWarp",
|
||||
"org.upscayl.Upscayl",
|
||||
"org.vinegarhq.Sober",
|
||||
"org.vinegarhq.Vinegar",
|
||||
"org.x.Warpinator",
|
||||
"page.codeberg.libre_menu_editor.LibreMenuEditor",
|
||||
"page.kramo.Cartridges",
|
||||
"page.tesk.Refine",
|
||||
"rocks.shy.VacuumTube",
|
||||
"ru.linux_gaming.PortProton",
|
||||
"ru.yandex.Browser",
|
||||
"tv.kodi.Kodi",
|
||||
"tv.plex.PlexDesktop",
|
||||
"xyz.ketok.Speedtest",
|
||||
"xyz.z3ntu.razergenie"
|
||||
]
|
||||
}
|
||||
38
src/lib/verified-snaps.json
Normal file
38
src/lib/verified-snaps.json
Normal file
@@ -0,0 +1,38 @@
|
||||
{
|
||||
"apps": [
|
||||
"firefox",
|
||||
"thunderbird",
|
||||
"chromium",
|
||||
"brave",
|
||||
"spotify",
|
||||
"code",
|
||||
"intellij-idea-community",
|
||||
"intellij-idea-ultimate",
|
||||
"pycharm-community",
|
||||
"pycharm-professional",
|
||||
"slack",
|
||||
"discord",
|
||||
"signal-desktop",
|
||||
"telegram-desktop",
|
||||
"zoom-client",
|
||||
"obsidian",
|
||||
"bitwarden",
|
||||
"blender",
|
||||
"gimp",
|
||||
"inkscape",
|
||||
"krita",
|
||||
"vlc",
|
||||
"obs-studio",
|
||||
"libreoffice",
|
||||
"node",
|
||||
"go",
|
||||
"rustup",
|
||||
"ruby",
|
||||
"cmake",
|
||||
"docker",
|
||||
"kubectl",
|
||||
"steam",
|
||||
"retroarch",
|
||||
"vivaldi"
|
||||
]
|
||||
}
|
||||
Reference in New Issue
Block a user