| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
Το README εξηγεί το install script και τις παραμέτρους του
Η ενότητα έδειχνε μόνο πώς καλείται. Τώρα λέει τι κάνει με τη σειρά, τι σημαίνει η κάθε παράμετρος, τι πρέπει να υπάρχει πριν (DNS) και τι μένει στο χέρι σου μετά (test-stream, R2, encoder). Και τη μία περίπτωση που σε κόβει στη μέση: το Server.host είναι unique, οπότε δεύτερο τρέξιμο με --panel/--key στο ίδιο hostname απορρίπτεται. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Τα API keys διαχειρίζονται από το /admin/apikeys
Μέχρι τώρα ήθελαν shell στον host (src/apikey.ts) — δηλαδή ssh για κάτι που είναι δουλειά του admin, όχι του μηχανήματος. Η οθόνη κάνει τα ίδια τρία: δημιουργία (η τιμή φαίνεται μία φορά), λίστα με την τελευταία χρήση, ανάκληση. Το CLI μένει: σε μηχάνημα με χαμένο κωδικό admin είναι ο μόνος δρόμος. Η μία διαφορά από το shell είναι σκόπιμη: ένα Bearer pk_… παίρνει 403 σε αυτά τα endpoints, παρόλο που ο ρόλος του είναι admin. Αλλιώς ένα κλειδί που διέρρευσε γεννάει διαδόχους και η ανάκληση δεν τελειώνει ποτέ — σβήνεις μία γραμμή και υπάρχουν ήδη άλλες δύο. Στο shell το ισοδύναμο είναι ότι έχεις ήδη root στο μηχάνημα. Χωρίς service class: τρία prisma calls χωρίς λογική ανάμεσα. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Οι κατάλογοι του admin γίνονται λίστα με σελιδοποίηση και ξεχωριστή κ…
…αρτέλα Πελάτες, πλάνα και servers ήταν μία κάρτα με φόρμες ανά εγγραφή, όλες ανοιχτές μαζί: με είκοσι πελάτες κάθε «Αποθήκευση» ήταν σε λάθος κάρτα ένα scroll μακριά και κάθε πεδίο που πληκτρολογείται ήθελε Record<number, …> για να ξεχωρίσει σε ποιον ανήκει. Τώρα ο καθένας είναι index.vue (λίστα, τρία κουμπιά ανά γραμμή) συν [id].vue (όλη η επεξεργασία της μίας εγγραφής) — τα routes δεν άλλαξαν. Το φίλτρο και η σελιδοποίηση ζουν στον browser (app/composables/usePaged.ts): οι τρεις κατάλογοι έρχονται ούτως ή άλλως ολόκληροι σε μία κλήση, ένα ?page= θα ζητούσε αλλαγή σε τρία endpoints για κάτι που δεν έχει σπάσει. Το test-paged.js κλειδώνει τους δύο σιωπηλούς τρόπους που σπάει μια τέτοια λίστα: φίλτρο από βαθιά σελίδα, και διαγραφή κάτω από τα πόδια της τρέχουσας σελίδας. Ο player του πελάτη μπαίνει ανά stream μέσα στην καρτέλα του — εκεί το «ποιο stream» απαντιέται μόνο του, ενώ στη λίστα δεν έχει απάντηση. Ίδιο modal με το /admin, οπότε το hlsUrl (τρίτος καλών πια) πήγε στο dash.ts. Ο επιλογέας server του dashboard διαβάζει ?host=, για να οδηγεί εκεί το κουμπί «στατιστικά» της λίστας των servers. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Το install φτιάχνει μόνο του τα DNS records στο Cloudflare
Δύο νέα flags, --cf-token και --cf-zone: το install στήνει τα δύο A records (hostname proxied, rtmp. DNS only γιατί το proxy δεν περνάει το 1935) πριν σηκωθεί ο Caddy — αλλιώς το ACME χτυπάει σε hostname που δεν υπάρχει ακόμα. Record που υπάρχει ήδη δεν πειράζεται, ώστε το install να ξανατρέχει· κάθε άλλη απόρριψη σταματάει την εγκατάσταση, γιατί χωρίς DNS ο server θα «εγκαθίστατο επιτυχώς» χωρίς να σηκώνει ούτε εκπομπή ούτε θεατή. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Ο δεύτερος έλεγχος ασφαλείας κλείνει τα ευρήματα του apps/api
Το JWT_SECRET δεν έχει πια αναδίπλωση: τυχαίο ≥32 χαρακτήρων ή η διεργασία δεν ξεκινάει (auth/secret.ts), με HS256 καρφωμένο σε sign και verify. Ένα fallback που «δουλεύει» σημαίνει server που υπογράφει admin tokens με μυστικό που διαβάζεται από το repo — και σιωπηλά. Γι' αυτό και το .env.example έχει κενή τιμή: παράδειγμα που δουλεύει είναι δημόσιο κλειδί υπογραφής. Τα μυστικά του stream server (token του sync, adminPass) φεύγουν μόνο στην απάντηση του POST /servers, όπως το κλειδί ενός API key — κανένα GET δεν τα ξαναδίνει, γιατί το GET το κάνει και κάθε API key. Το panel τα έδειχνε ήδη μία φορά, στη δημιουργία. Το login φρενάρει μετά από 10 συνεχόμενες αποτυχίες ανά IP+username (auth/throttle.ts) και το scrypt έγινε ασύγχρονο: κάθε προσπάθεια πλήρωνε έναν πυρήνα πάνω στον event loop. Μετράνε μόνο οι αποτυχίες και η επιτυχία μηδενίζει — όριο «Χ αιτήματα το λεπτό» θα έκοβε και τον πελάτη που δουλεύει. Μαζί, dummy hash για τον άγνωστο χρήστη (ο χρόνος απόκρισης έλεγε ποια ονόματα υπάρχουν) και ελάχιστο μήκος κωδικού μέσα στο hashPassword, ώστε να μην το ξεχάσει ένας από τους τρεις callers. Ένα API key έχει ρόλο admin αλλά δεν είναι άνθρωπος: το humanOnly βγήκε από το apikeys module (auth/human-only.ts) και κόβει και τα restart/kill session — το provisioning φτιάχνει πελάτες, δεν ρίχνει εκπομπές που παίζουν. Η αναστολή φαίνεται πια και στο API: το /me/* γίνεται read-only όσο ο πελάτης ή το πακέτο είναι σε αναστολή. Η εκπομπή έπεφτε σε ≤10s από το sync, αλλά ο πελάτης συνέχιζε να φτιάχνει streams και να ανανεώνει κλειδιά. Τα υπόλοιπα: ρητά πεδία αντί για spread του σώματος στο Prisma (άγνωστο κλειδί έβγαζε 500, γνωστό γραφόταν χωρίς να το ζητήσει κανείς), path χωρίς κομμάτια που αρχίζουν από τελεία, hostname προορισμού που αρχίζει από ψηφίο μόνο σε κανονική δεκαδική μορφή (το rtmp:// είναι non-special scheme, οπότε το 2130706433 έμενε αδιαφανές εδώ και γινόταν 127.0.0.1 στον ffmpeg), PANEL_URL από ρύθμιση αντί για το Host του caller, και USER node στο image. Εκτός apps/api: έφυγε το @hosterai/passwords (bcrypt -> node-pre-gyp -> tar, 1 critical/3 high) για δύο τυχαία strings που δίνει το node:crypto στο ίδιο αρχείο. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Merge branch 'feature/security-review-2' into develop
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Η καρτέλα του server δείχνει φόρτο, δίσκο και encoder του μηχανήματος
Ο διαχειριστής έμπαινε με ssh για τρία πράγματα που δεν φαίνονταν από πουθενά: πόσος δίσκος έμεινε (τον γεμίζουν τα segments), πόσο φορτωμένο είναι το μηχάνημα και ποιος encoder τρέχει όντως — το probe του boot πέφτει σιωπηλά σε x264 και το έλεγε μόνο μια γραμμή του log. Το `server` block του snapshot τα κουβαλάει και τα τρία, οπότε φτάνουν στο panel από το sync που ήδη τρέχει: κανένα νέο endpoint και κανένας δεύτερος δρόμος προς τον server. Ο δίσκος είναι `null` όταν ο φάκελος δεν υπάρχει ακόμα — ένα statfs που σκάει θα έριχνε ολόκληρο το snapshot, δηλαδή και το /admin και το sync μαζί. Στο /admin/servers/[id] μπαίνει κάρτα κατάστασης από το GET /live, με refresh στα 10s (ο ρυθμός του sync — πιο συχνά δεν υπάρχει τι να δει κανείς). Τα ιστορικά και το restart μένουν στο /admin, όπου ήταν πάντα. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Το log του stream server διαβάζεται από το panel, χωρίς ssh
Ό,τι γράφει ο server στην κονσόλα (respawn του ffmpeg, υποβάθμιση R2, ανακλήσεις publish) ζούσε μόνο πάνω στο μηχάνημα — και το πού ζει έχει άλλη απάντηση σε pm2 και άλλη σε docker. Γι' αυτό ο patch γίνεται στο ίδιο το console και όχι σε δικό μας logger: το log που θέλει ο διαχειριστής είναι ακριβώς αυτό που γράφεται ήδη, και μια δεύτερη συνάρτηση θα σήμαινε ότι όποια γραμμή ξεχάστηκε λείπει από το panel χωρίς να το ξέρει κανείς. Ring buffer 300 γραμμών, στη μνήμη και μόνο: χωρίς πλαφόν μια 24/7 εκπομπή είναι διαρροή μνήμης (ίδιος λόγος με το κλάδεμα των uploaded/fromOrigin στο r2.js), και αρχείο στον δίσκο θα ήταν τρίτο αντίγραφο ενός log που κρατάνε ήδη ο pm2 και ο docker. Restart του stream server = άδειο log, όπως ακριβώς και το /live. GET /admin/api/logs πίσω από το ίδιο basic auth με τα υπόλοιπα, proxy GET /servers/:host/logs στο api (καμία νέα διαδρομή προς τον server), και καρτέλα στο /admin/servers/[id] με τη νεότερη γραμμή πρώτη. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Το CLAUDE.md λέει πώς ενημερώνονται τα μηχανήματα χωρίς να πέσει πελάτης
Τα μηχανήματα δεν είναι πια demo: πάνω τους εκπέμπουν πελάτες, οπότε ο έλεγχος για ζωντανή εκπομπή πριν από κάθε restart του stream είναι μέρος της διαδικασίας και όχι προαίρεση — μέσα στο container δεν υπάρχει ps και το docker logs σιωπά όσο όλα πάνε καλά, άρα μόνο η ώρα του τελευταίου segment λέει την αλήθεια. Γράφονται και οι δύο σιωπηλές παγίδες που βγήκαν στην πράξη σε αυτή την ενημέρωση: το JWT_SECRET που ρίχνει τη διεργασία (και το μαθαίνεις όταν ο παλιός container έχει ήδη σβήσει), και το παλιό data volume που μένει root ενώ ο container τρέχει πια ως node — εκεί το API σηκώνεται κανονικά και μόνο τα γραψίματα σκάνε, δηλαδή το clients.json παγώνει και οι ανακλήσεις δεν φτάνουν ποτέ, χωρίς τίποτα να φαίνεται από μπροστά. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Ο stream server έγινε πλατφόρμα πολλών μηχανημάτων: το κεντρικό panel (apps/api + apps/panel) διαχειρίζεται πελάτες, πλάνα, συνδρομές και servers, και ο κάθε stream server συγχρονίζεται μαζί του ανά 10s. Το repo έγινε npm workspaces monorepo — ο stream server ζει πια στο apps/stream. Πελάτες: κλειδί εκπομπής ανά path, όριο θεατών ανά συνδρομή, αναστολή που πέφτει σε ≤10s χωρίς να χαθούν paths και κλειδιά, αναβάθμιση πακέτου επιτόπου. Ο πελάτης έχει δικό του panel — streams ανά συνδρομή, γραφήματα 1ώρας ως 30ημερών, οδηγίες OBS — και ο admin μπαίνει ως πελάτης χωρίς να αποσυνδεθεί. Εκπομπή: ABR (master playlist και variants από το ladder του πακέτου), επιλογή encoder ανά server (x264/QSV/NVENC), αναδιανομή σε YouTube και άλλες πλατφόρμες, και R2 sync που δεν παγώνει ποτέ το playlist — η υποβάθμιση φαίνεται στο /admin όσο συμβαίνει. Λειτουργία: API keys για provisioning, link σύνδεσης μιας χρήσης για το billing, φρενάρισμα του login, JWT_SECRET χωρίς αναδίπλωση, και η καρτέλα του κάθε server δείχνει φόρτο, δίσκο, encoder και το log του — χωρίς ssh. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Unfortunately it looks like we can’t render this comparison for you right now. It might be too big, or there might be something weird with your repository.
You can try running this command locally to see the comparison on your machine:
git diff feature/install-panel...develop
| Back | FazBrowse Home | New Git URL |