feat: consolidate setup scripts and guides into unified solution
Mirror to GitHub / mirror (push) Successful in 46s
Test and Lint / backend-test (push) Successful in 1m51s
Test and Lint / frontend-test (push) Successful in 2m17s
Version and Release / version-bump (push) Successful in 1m9s
Version and Release / trigger-drone (push) Successful in 3s

- Created unified SIMPLE_SETUP.md combining Docker and native installation guides
- Created universal scripts/setup.sh supporting both Docker and native installations
- Removed redundant setup files (simple-setup.md, simple-setup.sh, scripts/simple-setup.sh)
- Added intelligent installation method selection based on system resources
- Implemented update and uninstall functionality in unified script
- Enhanced with command-line options for unattended installations
- Improved cross-platform support (Ubuntu, Debian, RHEL/CentOS, Fedora, Raspberry Pi OS)

Fixes #7

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2025-08-29 21:59:39 +02:00
parent a73d217273
commit 29a8ff914c
6 changed files with 1924 additions and 1594 deletions
+196 -376
View File
@@ -1,518 +1,338 @@
[
{
"timestamp": 1756069288374,
"timestamp": 1756496746582,
"memoryTotal": 25769803776,
"memoryUsed": 24624726016,
"memoryFree": 1145077760,
"memoryUsagePercent": 95.55651346842447,
"memoryEfficiency": 4.44348653157553,
"memoryUsed": 25277186048,
"memoryFree": 492617728,
"memoryUsagePercent": 98.08839162190756,
"memoryEfficiency": 1.9116083780924384,
"cpuCount": 14,
"cpuLoad": 0.20016043526785715,
"cpuLoad": 0.24197823660714285,
"platform": "darwin",
"uptime": 4755441
"uptime": 5182900
},
{
"timestamp": 1756069318375,
"timestamp": 1756496776582,
"memoryTotal": 25769803776,
"memoryUsed": 25187663872,
"memoryFree": 582139904,
"memoryUsagePercent": 97.74099985758463,
"memoryEfficiency": 2.259000142415374,
"memoryUsed": 25447661568,
"memoryFree": 322142208,
"memoryUsagePercent": 98.74992370605469,
"memoryEfficiency": 1.2500762939453125,
"cpuCount": 14,
"cpuLoad": 0.17435128348214285,
"cpuLoad": 0.1962890625,
"platform": "darwin",
"uptime": 4755471
"uptime": 5182930
},
{
"timestamp": 1756069348375,
"timestamp": 1756496806600,
"memoryTotal": 25769803776,
"memoryUsed": 25293045760,
"memoryFree": 476758016,
"memoryUsagePercent": 98.14993540445963,
"memoryEfficiency": 1.850064595540374,
"memoryUsed": 25413042176,
"memoryFree": 356761600,
"memoryUsagePercent": 98.61558278401694,
"memoryEfficiency": 1.3844172159830634,
"cpuCount": 14,
"cpuLoad": 0.18059430803571427,
"cpuLoad": 0.22129603794642858,
"platform": "darwin",
"uptime": 4755501
"uptime": 5182960
},
{
"timestamp": 1756069378375,
"timestamp": 1756496836604,
"memoryTotal": 25769803776,
"memoryUsed": 25323126784,
"memoryFree": 446676992,
"memoryUsagePercent": 98.26666514078775,
"memoryEfficiency": 1.733334859212249,
"memoryUsed": 25516834816,
"memoryFree": 252968960,
"memoryUsagePercent": 99.01835123697916,
"memoryEfficiency": 0.9816487630208428,
"cpuCount": 14,
"cpuLoad": 0.13584681919642858,
"cpuLoad": 0.21344866071428573,
"platform": "darwin",
"uptime": 4755531
"uptime": 5182990
},
{
"timestamp": 1756069408378,
"timestamp": 1756496866605,
"memoryTotal": 25769803776,
"memoryUsed": 25281445888,
"memoryFree": 488357888,
"memoryUsagePercent": 98.10492197672525,
"memoryEfficiency": 1.895078023274749,
"memoryUsed": 25414123520,
"memoryFree": 355680256,
"memoryUsagePercent": 98.61977895100912,
"memoryEfficiency": 1.380221048990876,
"cpuCount": 14,
"cpuLoad": 0.13201032366071427,
"cpuLoad": 0.21822684151785715,
"platform": "darwin",
"uptime": 4755561
"uptime": 5183020
},
{
"timestamp": 1756069438379,
"timestamp": 1756496896607,
"memoryTotal": 25769803776,
"memoryUsed": 25302237184,
"memoryFree": 467566592,
"memoryUsagePercent": 98.18560282389322,
"memoryEfficiency": 1.8143971761067803,
"memoryUsed": 25196363776,
"memoryFree": 573440000,
"memoryUsagePercent": 97.77475992838541,
"memoryEfficiency": 2.225240071614593,
"cpuCount": 14,
"cpuLoad": 0.15656389508928573,
"cpuLoad": 0.20950753348214285,
"platform": "darwin",
"uptime": 4755591
"uptime": 5183050
},
{
"timestamp": 1756069468380,
"timestamp": 1756496926609,
"memoryTotal": 25769803776,
"memoryUsed": 25389481984,
"memoryFree": 380321792,
"memoryUsagePercent": 98.52415720621744,
"memoryEfficiency": 1.4758427937825616,
"memoryUsed": 25503711232,
"memoryFree": 266092544,
"memoryUsagePercent": 98.96742502848306,
"memoryEfficiency": 1.0325749715169366,
"cpuCount": 14,
"cpuLoad": 0.190185546875,
"cpuLoad": 0.23154994419642858,
"platform": "darwin",
"uptime": 4755621
"uptime": 5183080
},
{
"timestamp": 1756069498381,
"timestamp": 1756496956610,
"memoryTotal": 25769803776,
"memoryUsed": 25366888448,
"memoryFree": 402915328,
"memoryUsagePercent": 98.43648274739584,
"memoryEfficiency": 1.5635172526041572,
"memoryUsed": 25550979072,
"memoryFree": 218824704,
"memoryUsagePercent": 99.15084838867188,
"memoryEfficiency": 0.849151611328125,
"cpuCount": 14,
"cpuLoad": 0.16587611607142858,
"cpuLoad": 0.20741489955357142,
"platform": "darwin",
"uptime": 4755651
"uptime": 5183110
},
{
"timestamp": 1756069528382,
"timestamp": 1756496986609,
"memoryTotal": 25769803776,
"memoryUsed": 25608175616,
"memoryFree": 161628160,
"memoryUsagePercent": 99.3728001912435,
"memoryEfficiency": 0.6271998087565009,
"memoryUsed": 25552404480,
"memoryFree": 217399296,
"memoryUsagePercent": 99.15637969970703,
"memoryEfficiency": 0.8436203002929688,
"cpuCount": 14,
"cpuLoad": 0.17508370535714285,
"cpuLoad": 0.22112165178571427,
"platform": "darwin",
"uptime": 4755681
"uptime": 5183140
},
{
"timestamp": 1756069558384,
"timestamp": 1756497016611,
"memoryTotal": 25769803776,
"memoryUsed": 25126961152,
"memoryFree": 642842624,
"memoryUsagePercent": 97.50544230143228,
"memoryEfficiency": 2.494557698567718,
"memoryUsed": 25680560128,
"memoryFree": 89243648,
"memoryUsagePercent": 99.653689066569,
"memoryEfficiency": 0.34631093343099906,
"cpuCount": 14,
"cpuLoad": 0.17497907366071427,
"cpuLoad": 0.23615373883928573,
"platform": "darwin",
"uptime": 4755711
"uptime": 5183170
},
{
"timestamp": 1756069588385,
"timestamp": 1756497046610,
"memoryTotal": 25769803776,
"memoryUsed": 25449037824,
"memoryFree": 320765952,
"memoryUsagePercent": 98.75526428222656,
"memoryEfficiency": 1.2447357177734375,
"memoryUsed": 25631408128,
"memoryFree": 138395648,
"memoryUsagePercent": 99.46295420328775,
"memoryEfficiency": 0.5370457967122491,
"cpuCount": 14,
"cpuLoad": 0.149169921875,
"cpuLoad": 0.19859095982142858,
"platform": "darwin",
"uptime": 4755741
"uptime": 5183200
},
{
"timestamp": 1756069618386,
"timestamp": 1756497076611,
"memoryTotal": 25769803776,
"memoryUsed": 25644417024,
"memoryFree": 125386752,
"memoryUsagePercent": 99.51343536376953,
"memoryEfficiency": 0.48656463623046875,
"memoryUsed": 25655099392,
"memoryFree": 114704384,
"memoryUsagePercent": 99.55488840738931,
"memoryEfficiency": 0.44511159261068656,
"cpuCount": 14,
"cpuLoad": 0.16245814732142858,
"cpuLoad": 0.23831612723214285,
"platform": "darwin",
"uptime": 4755771
"uptime": 5183230
},
{
"timestamp": 1756069648386,
"timestamp": 1756497106613,
"memoryTotal": 25769803776,
"memoryUsed": 25567952896,
"memoryFree": 201850880,
"memoryUsagePercent": 99.21671549479166,
"memoryEfficiency": 0.7832845052083428,
"memoryUsed": 25590169600,
"memoryFree": 179634176,
"memoryUsagePercent": 99.30292765299478,
"memoryEfficiency": 0.6970723470052178,
"cpuCount": 14,
"cpuLoad": 0.17110770089285715,
"cpuLoad": 0.29031808035714285,
"platform": "darwin",
"uptime": 4755801
"uptime": 5183260
},
{
"timestamp": 1756069678387,
"timestamp": 1756497136613,
"memoryTotal": 25769803776,
"memoryUsed": 25535414272,
"memoryFree": 234389504,
"memoryUsagePercent": 99.09044901529947,
"memoryEfficiency": 0.9095509847005303,
"memoryUsed": 25682378752,
"memoryFree": 87425024,
"memoryUsagePercent": 99.66074625651041,
"memoryEfficiency": 0.3392537434895928,
"cpuCount": 14,
"cpuLoad": 0.17755998883928573,
"cpuLoad": 0.26834542410714285,
"platform": "darwin",
"uptime": 4755831
"uptime": 5183290
},
{
"timestamp": 1756069708388,
"timestamp": 1756497166614,
"memoryTotal": 25769803776,
"memoryUsed": 25529073664,
"memoryFree": 240730112,
"memoryUsagePercent": 99.06584421793619,
"memoryEfficiency": 0.9341557820638116,
"memoryUsed": 25591283712,
"memoryFree": 178520064,
"memoryUsagePercent": 99.3072509765625,
"memoryEfficiency": 0.6927490234375,
"cpuCount": 14,
"cpuLoad": 0.18059430803571427,
"cpuLoad": 0.2820521763392857,
"platform": "darwin",
"uptime": 4755861
"uptime": 5183320
},
{
"timestamp": 1756069738390,
"timestamp": 1756497196616,
"memoryTotal": 25769803776,
"memoryUsed": 25574866944,
"memoryFree": 194936832,
"memoryUsagePercent": 99.24354553222656,
"memoryEfficiency": 0.7564544677734375,
"memoryUsed": 25577848832,
"memoryFree": 191954944,
"memoryUsagePercent": 99.25511678059897,
"memoryEfficiency": 0.7448832194010322,
"cpuCount": 14,
"cpuLoad": 0.18073381696428573,
"cpuLoad": 0.23618861607142858,
"platform": "darwin",
"uptime": 4755891
"uptime": 5183350
},
{
"timestamp": 1756069768392,
"timestamp": 1756497226617,
"memoryTotal": 25769803776,
"memoryUsed": 25546063872,
"memoryFree": 223739904,
"memoryUsagePercent": 99.13177490234375,
"memoryEfficiency": 0.86822509765625,
"memoryUsed": 25343754240,
"memoryFree": 426049536,
"memoryUsagePercent": 98.34671020507812,
"memoryEfficiency": 1.653289794921875,
"cpuCount": 14,
"cpuLoad": 0.18244280133928573,
"cpuLoad": 0.23228236607142858,
"platform": "darwin",
"uptime": 4755921
"uptime": 5183380
},
{
"timestamp": 1756069798393,
"timestamp": 1756497256617,
"memoryTotal": 25769803776,
"memoryUsed": 25663946752,
"memoryFree": 105857024,
"memoryUsagePercent": 99.58922068277994,
"memoryEfficiency": 0.41077931722006156,
"memoryUsed": 25662668800,
"memoryFree": 107134976,
"memoryUsagePercent": 99.58426157633463,
"memoryEfficiency": 0.41573842366537406,
"cpuCount": 14,
"cpuLoad": 0.15778459821428573,
"cpuLoad": 0.19471958705357142,
"platform": "darwin",
"uptime": 4755951
"uptime": 5183410
},
{
"timestamp": 1756069828394,
"timestamp": 1756497286619,
"memoryTotal": 25769803776,
"memoryUsed": 25486589952,
"memoryFree": 283213824,
"memoryUsagePercent": 98.90098571777344,
"memoryEfficiency": 1.0990142822265625,
"memoryUsed": 25610125312,
"memoryFree": 159678464,
"memoryUsagePercent": 99.38036600748697,
"memoryEfficiency": 0.6196339925130303,
"cpuCount": 14,
"cpuLoad": 0.19245256696428573,
"cpuLoad": 0.19126674107142858,
"platform": "darwin",
"uptime": 4755981
"uptime": 5183440
},
{
"timestamp": 1756069858395,
"timestamp": 1756497316620,
"memoryTotal": 25769803776,
"memoryUsed": 25451380736,
"memoryFree": 318423040,
"memoryUsagePercent": 98.76435597737631,
"memoryEfficiency": 1.2356440226236884,
"memoryUsed": 25256427520,
"memoryFree": 513376256,
"memoryUsagePercent": 98.0078379313151,
"memoryEfficiency": 1.9921620686849053,
"cpuCount": 14,
"cpuLoad": 0.17762974330357142,
"cpuLoad": 0.15576171875,
"platform": "darwin",
"uptime": 4756011
"uptime": 5183470
},
{
"timestamp": 1756069888397,
"timestamp": 1756497346620,
"memoryTotal": 25769803776,
"memoryUsed": 25088442368,
"memoryFree": 681361408,
"memoryUsagePercent": 97.35596974690756,
"memoryEfficiency": 2.6440302530924384,
"memoryUsed": 25464700928,
"memoryFree": 305102848,
"memoryUsagePercent": 98.81604512532553,
"memoryEfficiency": 1.1839548746744697,
"cpuCount": 14,
"cpuLoad": 0.159912109375,
"cpuLoad": 0.21369280133928573,
"platform": "darwin",
"uptime": 4756041
"uptime": 5183500
},
{
"timestamp": 1756069918398,
"timestamp": 1756497376620,
"memoryTotal": 25769803776,
"memoryUsed": 25246859264,
"memoryFree": 522944512,
"memoryUsagePercent": 97.97070821126303,
"memoryEfficiency": 2.0292917887369697,
"memoryUsed": 25632653312,
"memoryFree": 137150464,
"memoryUsagePercent": 99.46778615315756,
"memoryEfficiency": 0.5322138468424384,
"cpuCount": 14,
"cpuLoad": 0.12831333705357142,
"cpuLoad": 0.201904296875,
"platform": "darwin",
"uptime": 4756071
"uptime": 5183530
},
{
"timestamp": 1756069948399,
"timestamp": 1756497406621,
"memoryTotal": 25769803776,
"memoryUsed": 25182126080,
"memoryFree": 587677696,
"memoryUsagePercent": 97.71951039632162,
"memoryEfficiency": 2.280489603678376,
"memoryUsed": 25066160128,
"memoryFree": 703643648,
"memoryUsagePercent": 97.26950327555338,
"memoryEfficiency": 2.730496724446624,
"cpuCount": 14,
"cpuLoad": 0.1826171875,
"cpuLoad": 0.192626953125,
"platform": "darwin",
"uptime": 4756101
"uptime": 5183560
},
{
"timestamp": 1756069978401,
"timestamp": 1756497436623,
"memoryTotal": 25769803776,
"memoryUsed": 25322684416,
"memoryFree": 447119360,
"memoryUsagePercent": 98.26494852701822,
"memoryEfficiency": 1.7350514729817803,
"memoryUsed": 25574244352,
"memoryFree": 195559424,
"memoryUsagePercent": 99.24112955729166,
"memoryEfficiency": 0.7588704427083428,
"cpuCount": 14,
"cpuLoad": 0.18136160714285715,
"cpuLoad": 0.19761439732142858,
"platform": "darwin",
"uptime": 4756131
"uptime": 5183590
},
{
"timestamp": 1756070008402,
"timestamp": 1756497466624,
"memoryTotal": 25769803776,
"memoryUsed": 25357484032,
"memoryFree": 412319744,
"memoryUsagePercent": 98.39998881022134,
"memoryEfficiency": 1.6000111897786553,
"memoryUsed": 25427623936,
"memoryFree": 342179840,
"memoryUsagePercent": 98.67216746012369,
"memoryEfficiency": 1.3278325398763116,
"cpuCount": 14,
"cpuLoad": 0.18488420758928573,
"cpuLoad": 0.20005580357142858,
"platform": "darwin",
"uptime": 4756161
"uptime": 5183620
},
{
"timestamp": 1756070038402,
"timestamp": 1756497496626,
"memoryTotal": 25769803776,
"memoryUsed": 25503760384,
"memoryFree": 266043392,
"memoryUsagePercent": 98.96761576334634,
"memoryEfficiency": 1.0323842366536553,
"memoryUsed": 25596116992,
"memoryFree": 173686784,
"memoryUsagePercent": 99.32600657145181,
"memoryEfficiency": 0.6739934285481866,
"cpuCount": 14,
"cpuLoad": 0.18415178571428573,
"cpuLoad": 0.19367327008928573,
"platform": "darwin",
"uptime": 4756191
"uptime": 5183650
},
{
"timestamp": 1756070068403,
"timestamp": 1756497526626,
"memoryTotal": 25769803776,
"memoryUsed": 25171984384,
"memoryFree": 597819392,
"memoryUsagePercent": 97.68015543619791,
"memoryEfficiency": 2.319844563802093,
"memoryUsed": 25207111680,
"memoryFree": 562692096,
"memoryUsagePercent": 97.81646728515625,
"memoryEfficiency": 2.18353271484375,
"cpuCount": 14,
"cpuLoad": 0.22530691964285715,
"cpuLoad": 0.15276227678571427,
"platform": "darwin",
"uptime": 4756221
"uptime": 5183680
},
{
"timestamp": 1756070098405,
"timestamp": 1756497556628,
"memoryTotal": 25769803776,
"memoryUsed": 25521356800,
"memoryFree": 248446976,
"memoryUsagePercent": 99.03589884440103,
"memoryEfficiency": 0.9641011555989678,
"memoryUsed": 25510182912,
"memoryFree": 259620864,
"memoryUsagePercent": 98.99253845214844,
"memoryEfficiency": 1.0074615478515625,
"cpuCount": 14,
"cpuLoad": 0.20929827008928573,
"cpuLoad": 0.13654436383928573,
"platform": "darwin",
"uptime": 4756251
},
{
"timestamp": 1756070128388,
"memoryTotal": 25769803776,
"memoryUsed": 25556877312,
"memoryFree": 212926464,
"memoryUsagePercent": 99.17373657226562,
"memoryEfficiency": 0.826263427734375,
"cpuCount": 14,
"cpuLoad": 0.19838169642857142,
"platform": "darwin",
"uptime": 4756281
},
{
"timestamp": 1756070158382,
"memoryTotal": 25769803776,
"memoryUsed": 25423691776,
"memoryFree": 346112000,
"memoryUsagePercent": 98.65690867106119,
"memoryEfficiency": 1.3430913289388116,
"cpuCount": 14,
"cpuLoad": 0.23814174107142858,
"platform": "darwin",
"uptime": 4756311
},
{
"timestamp": 1756070188381,
"memoryTotal": 25769803776,
"memoryUsed": 24874237952,
"memoryFree": 895565824,
"memoryUsagePercent": 96.52474721272787,
"memoryEfficiency": 3.475252787272126,
"cpuCount": 14,
"cpuLoad": 0.22272600446428573,
"platform": "darwin",
"uptime": 4756341
},
{
"timestamp": 1756070218381,
"memoryTotal": 25769803776,
"memoryUsed": 24984682496,
"memoryFree": 785121280,
"memoryUsagePercent": 96.95332845052084,
"memoryEfficiency": 3.046671549479157,
"cpuCount": 14,
"cpuLoad": 0.23807198660714285,
"platform": "darwin",
"uptime": 4756371
},
{
"timestamp": 1756070248383,
"memoryTotal": 25769803776,
"memoryUsed": 25002754048,
"memoryFree": 767049728,
"memoryUsagePercent": 97.02345530192056,
"memoryEfficiency": 2.9765446980794366,
"cpuCount": 14,
"cpuLoad": 0.22795758928571427,
"platform": "darwin",
"uptime": 4756401
},
{
"timestamp": 1756070278384,
"memoryTotal": 25769803776,
"memoryUsed": 25581617152,
"memoryFree": 188186624,
"memoryUsagePercent": 99.26973978678384,
"memoryEfficiency": 0.7302602132161553,
"cpuCount": 14,
"cpuLoad": 0.19461495535714285,
"platform": "darwin",
"uptime": 4756431
},
{
"timestamp": 1756070308384,
"memoryTotal": 25769803776,
"memoryUsed": 25362219008,
"memoryFree": 407584768,
"memoryUsagePercent": 98.41836293538412,
"memoryEfficiency": 1.581637064615876,
"cpuCount": 14,
"cpuLoad": 0.23835100446428573,
"platform": "darwin",
"uptime": 4756461
},
{
"timestamp": 1756070338385,
"memoryTotal": 25769803776,
"memoryUsed": 25515917312,
"memoryFree": 253886464,
"memoryUsagePercent": 99.01479085286459,
"memoryEfficiency": 0.9852091471354072,
"cpuCount": 14,
"cpuLoad": 0.189208984375,
"platform": "darwin",
"uptime": 4756491
},
{
"timestamp": 1756070368387,
"memoryTotal": 25769803776,
"memoryUsed": 25517277184,
"memoryFree": 252526592,
"memoryUsagePercent": 99.02006785074869,
"memoryEfficiency": 0.9799321492513116,
"cpuCount": 14,
"cpuLoad": 0.22147042410714285,
"platform": "darwin",
"uptime": 4756521
},
{
"timestamp": 1756070398388,
"memoryTotal": 25769803776,
"memoryUsed": 25596608512,
"memoryFree": 173195264,
"memoryUsagePercent": 99.32791392008463,
"memoryEfficiency": 0.6720860799153741,
"cpuCount": 14,
"cpuLoad": 0.16423688616071427,
"platform": "darwin",
"uptime": 4756551
},
{
"timestamp": 1756070428388,
"memoryTotal": 25769803776,
"memoryUsed": 25535250432,
"memoryFree": 234553344,
"memoryUsagePercent": 99.08981323242188,
"memoryEfficiency": 0.910186767578125,
"cpuCount": 14,
"cpuLoad": 0.14815848214285715,
"platform": "darwin",
"uptime": 4756581
},
{
"timestamp": 1756070458390,
"memoryTotal": 25769803776,
"memoryUsed": 25610747904,
"memoryFree": 159055872,
"memoryUsagePercent": 99.38278198242188,
"memoryEfficiency": 0.617218017578125,
"cpuCount": 14,
"cpuLoad": 0.12758091517857142,
"platform": "darwin",
"uptime": 4756611
},
{
"timestamp": 1756070488391,
"memoryTotal": 25769803776,
"memoryUsed": 25343475712,
"memoryFree": 426328064,
"memoryUsagePercent": 98.34562937418619,
"memoryEfficiency": 1.6543706258138116,
"cpuCount": 14,
"cpuLoad": 0.13835797991071427,
"platform": "darwin",
"uptime": 4756641
},
{
"timestamp": 1756070518391,
"memoryTotal": 25769803776,
"memoryUsed": 25244123136,
"memoryFree": 525680640,
"memoryUsagePercent": 97.96009063720703,
"memoryEfficiency": 2.0399093627929688,
"cpuCount": 14,
"cpuLoad": 0.11725725446428571,
"platform": "darwin",
"uptime": 4756671
},
{
"timestamp": 1756070548393,
"memoryTotal": 25769803776,
"memoryUsed": 25330614272,
"memoryFree": 439189504,
"memoryUsagePercent": 98.29572041829428,
"memoryEfficiency": 1.7042795817057197,
"cpuCount": 14,
"cpuLoad": 0.151611328125,
"platform": "darwin",
"uptime": 4756701
"uptime": 5183710
}
]
+25 -1
View File
@@ -1,6 +1,30 @@
# 🚀 PicPeak Deployment Guide
This guide covers deploying PicPeak using Docker Compose with direct port exposure. For internet-facing deployments, you'll need to add a reverse proxy (nginx, Traefik, Caddy, etc.) for SSL/HTTPS.
This guide covers multiple deployment options for PicPeak, from simple local setups to production-ready configurations.
## 🎯 Quick Start - Simple Setup (Recommended for Beginners)
For the easiest installation without Docker or complex configurations, use our **simple setup script**:
```bash
curl -fsSL https://raw.githubusercontent.com/yourusername/wedding-photo-sharing/main/scripts/simple-setup.sh -o setup.sh && \
chmod +x setup.sh && \
sudo ./setup.sh
```
This automated script handles everything including OS detection, dependencies, database setup, and service configuration. Perfect for:
- Small to medium deployments
- Local or VPS installations
- Users who prefer avoiding Docker complexity
- Quick testing and evaluation
👉 **See [SIMPLE_SETUP_GUIDE.md](./SIMPLE_SETUP_GUIDE.md) for detailed instructions.**
---
## 🐳 Docker Compose Deployment
This section covers deploying PicPeak using Docker Compose with direct port exposure. For internet-facing deployments, you'll need to add a reverse proxy (nginx, Traefik, Caddy, etc.) for SSL/HTTPS.
## 📋 Table of Contents
+519
View File
@@ -0,0 +1,519 @@
# 🚀 PicPeak Simple Setup Guide
This guide provides easy installation instructions for PicPeak on Linux servers with both Docker and non-Docker options.
## 📋 Quick Start
### One-Line Installation
```bash
# Download and run the unified setup script
curl -fsSL https://raw.githubusercontent.com/yourusername/wedding-photo-sharing/main/scripts/setup.sh -o setup.sh && \
chmod +x setup.sh && \
sudo ./setup.sh
```
The script will automatically detect your environment and recommend the best installation method.
## 🎯 Installation Methods
### Method 1: Docker Installation (Recommended)
Best for: Most users, easy updates, isolated environment
```bash
sudo ./setup.sh --docker
```
**Pros:**
- ✅ Easier installation and updates
- ✅ Better isolation from system
- ✅ Consistent environment across platforms
- ✅ Built-in PostgreSQL and Redis
**Cons:**
- ❌ Requires more resources (~4GB RAM recommended)
- ❌ Additional Docker overhead
### Method 2: Native Installation
Best for: Resource-constrained systems, Raspberry Pi, direct control
```bash
sudo ./setup.sh --native
```
**Pros:**
- ✅ Lower resource usage (~1GB RAM minimum)
- ✅ Direct system control
- ✅ No Docker overhead
- ✅ Better for ARM devices
**Cons:**
- ❌ More complex setup
- ❌ System dependencies required
- ❌ Manual update process
## 📋 System Requirements
### Minimum Requirements
- **OS**: Ubuntu 20.04+, Debian 11+, Fedora 38+, RHEL/CentOS 8+, Raspberry Pi OS
- **RAM**:
- Docker: 2GB minimum (4GB recommended)
- Native: 1GB minimum (2GB recommended)
- **Storage**: 2GB for application + space for photos
- **Network**: Port 3001 (or 80/443 with proxy)
### Supported Platforms
- ✅ Ubuntu 20.04, 22.04, 24.04
- ✅ Debian 11, 12
- ✅ Raspberry Pi OS (32-bit and 64-bit)
- ✅ Fedora 38, 39, 40
- ✅ RHEL/CentOS/Rocky/AlmaLinux 8, 9
## 🛠️ Installation Options
### Interactive Mode (Default)
```bash
sudo ./setup.sh
```
The script will prompt you to choose:
1. Installation method (Docker or Native)
2. Admin email and password
3. Domain configuration (optional)
4. Email server settings (optional)
5. SSL/HTTPS setup (optional)
### Unattended Installation
#### Docker with full configuration:
```bash
sudo ./setup.sh --docker --unattended \
--domain photos.example.com \
--email admin@example.com \
--admin-password SecurePass123 \
--smtp-host smtp.gmail.com \
--smtp-port 587 \
--smtp-user your-email@gmail.com \
--smtp-pass your-app-password \
--enable-ssl
```
#### Native with minimal configuration:
```bash
sudo ./setup.sh --native --unattended \
--email admin@example.com \
--admin-password SecurePass123
```
### Command Line Options
| Option | Description | Example |
|--------|-------------|---------|
| `--docker` | Use Docker installation | `--docker` |
| `--native` | Use native installation | `--native` |
| `--unattended` | Run without prompts | `--unattended` |
| `--domain` | Domain for HTTPS setup | `--domain photos.example.com` |
| `--email` | Admin email address | `--email admin@example.com` |
| `--admin-password` | Set admin password | `--admin-password MySecurePass` |
| `--smtp-host` | SMTP server hostname | `--smtp-host smtp.gmail.com` |
| `--smtp-port` | SMTP server port | `--smtp-port 587` |
| `--smtp-user` | SMTP username | `--smtp-user user@gmail.com` |
| `--smtp-pass` | SMTP password | `--smtp-pass app-password` |
| `--enable-ssl` | Enable HTTPS with Let's Encrypt | `--enable-ssl` |
| `--port` | Custom port (native only) | `--port 8080` |
| `--update` | Update existing installation | `--update` |
| `--uninstall` | Remove installation | `--uninstall` |
| `--help` | Show help message | `--help` |
## 🏗️ What Gets Installed
### Docker Installation
```
~/picpeak/ # Or custom directory
├── docker-compose.yml # Service definitions
├── .env # Configuration
├── storage/
│ └── events/ # Photo storage
│ ├── active/ # Current galleries
│ └── archived/ # Expired galleries
├── logs/ # Application logs
└── backup/ # Backup directory
```
**Services:**
- PicPeak Backend (Node.js application)
- PostgreSQL Database
- Redis Cache
- Nginx Reverse Proxy (optional)
- Background Workers
### Native Installation
```
/opt/picpeak/ # Installation directory
├── backend/ # Application code
├── events/ # Photo storage
│ ├── active/ # Current galleries
│ └── archived/ # Expired galleries
├── logs/ # Application logs
└── config/ # Configuration files
```
**Services (systemd):**
- `picpeak-backend` - Main application
- `picpeak-workers` - Background workers
- `caddy` - Web server (optional)
## 🌐 Access Methods
### Direct Access (Simplest)
- **Docker**: `http://your-server:3000` (frontend), `http://your-server:3001/admin` (admin)
- **Native**: `http://your-server:3001/admin` (admin panel)
### With Domain & HTTPS
If configured during setup:
- `https://your-domain.com` - Gallery frontend
- `https://your-domain.com/admin` - Admin panel
### Behind Existing Proxy
Add to your Nginx/Apache configuration:
```nginx
location / {
proxy_pass http://localhost:3001;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
client_max_body_size 100M;
}
```
## 📁 Managing Galleries
### Creating a Gallery
#### Method 1: Via Admin Panel (Recommended)
1. Login to admin panel
2. Click "Create New Event"
3. Configure settings and upload photos
#### Method 2: File System
```bash
# Docker installation
mkdir -p ~/picpeak/storage/events/active/wedding-smith-2024
cp /path/to/photos/* ~/picpeak/storage/events/active/wedding-smith-2024/
# Native installation
sudo mkdir -p /opt/picpeak/events/active/wedding-smith-2024
sudo cp /path/to/photos/* /opt/picpeak/events/active/wedding-smith-2024/
sudo chown -R picpeak:picpeak /opt/picpeak/events/active/wedding-smith-2024
```
### Gallery Structure
```
wedding-smith-2024/
├── collages/ # Group photos
├── individual/ # Individual photos
└── thumbnails/ # Auto-generated thumbnails
```
## 🔧 Service Management
### Docker Installation
```bash
cd ~/picpeak
# Check status
docker compose ps
# View logs
docker compose logs -f
# Stop services
docker compose down
# Start services
docker compose up -d
# Restart services
docker compose restart
# Update PicPeak
docker compose pull
docker compose up -d
```
### Native Installation
```bash
# Check status
sudo systemctl status picpeak-backend
sudo systemctl status picpeak-workers
# View logs
sudo journalctl -u picpeak-backend -f
sudo journalctl -u picpeak-workers -f
# Start services
sudo systemctl start picpeak-backend picpeak-workers
# Stop services
sudo systemctl stop picpeak-backend picpeak-workers
# Restart services
sudo systemctl restart picpeak-backend picpeak-workers
# Update PicPeak
sudo ./setup.sh --update
```
## ⚙️ Configuration
### Docker Configuration
Edit `~/picpeak/.env`:
```bash
nano ~/picpeak/.env
docker compose restart
```
### Native Configuration
Edit `/opt/picpeak/backend/.env`:
```bash
sudo nano /opt/picpeak/backend/.env
sudo systemctl restart picpeak-backend
```
### Key Settings
| Setting | Description | Default |
|---------|-------------|---------|
| `JWT_SECRET` | Token signing secret | Auto-generated |
| `ADMIN_EMAIL` | Admin email | admin@example.com |
| `ADMIN_PASSWORD` | Admin password | Auto-generated |
| `PHOTOS_DIR` | Photo storage path | Varies by method |
| `SMTP_ENABLED` | Email notifications | false |
| `DEFAULT_EXPIRY_DAYS` | Gallery expiration | 30 |
## 📧 Email Configuration
### Gmail Setup
1. Enable 2-Factor Authentication
2. Generate App Password
3. Configure:
```env
SMTP_ENABLED=true
SMTP_HOST=smtp.gmail.com
SMTP_PORT=587
SMTP_SECURE=false
SMTP_USER=your-email@gmail.com
SMTP_PASS=your-app-password
SMTP_FROM=noreply@yourdomain.com
```
### SendGrid Setup
1. Sign up at sendgrid.com (100 emails/day free)
2. Create API key
3. Configure:
```env
SMTP_ENABLED=true
SMTP_HOST=smtp.sendgrid.net
SMTP_PORT=587
SMTP_USER=apikey
SMTP_PASS=your-sendgrid-api-key
SMTP_FROM=verified-sender@yourdomain.com
```
## 🔄 Maintenance
### Backups
#### Docker:
```bash
# Backup script included
cd ~/picpeak
./backup.sh
# Manual backup
docker exec picpeak-postgres pg_dump -U picpeak picpeak > backup.sql
tar -czf photos-backup.tar.gz storage/events/
```
#### Native:
```bash
# Database backup
sudo cp /opt/picpeak/backend/database.sqlite /backup/database-$(date +%Y%m%d).sqlite
# Photos backup
sudo tar -czf /backup/photos-$(date +%Y%m%d).tar.gz /opt/picpeak/events/
```
### Updates
```bash
# Docker
cd ~/picpeak
docker compose pull
docker compose up -d
# Native
sudo ./setup.sh --update
```
### Uninstall
```bash
# Will prompt for confirmation and data removal options
sudo ./setup.sh --uninstall
```
## 🐛 Troubleshooting
### Common Issues
#### Service Won't Start
```bash
# Docker
docker compose logs backend
docker compose down && docker compose up -d
# Native
sudo journalctl -u picpeak-backend -n 50
sudo systemctl restart picpeak-backend
```
#### Can't Access Admin Panel
1. Check firewall:
```bash
# Ubuntu/Debian
sudo ufw allow 3001
# RHEL/CentOS
sudo firewall-cmd --add-port=3001/tcp --permanent
sudo firewall-cmd --reload
```
2. Verify service:
```bash
# Docker
curl http://localhost:3001/api/health
# Native
sudo systemctl is-active picpeak-backend
```
#### Photos Not Showing
```bash
# Check permissions (Native)
sudo chown -R picpeak:picpeak /opt/picpeak/events/
sudo chmod -R 755 /opt/picpeak/events/
# Check permissions (Docker)
ls -la ~/picpeak/storage/events/
```
#### Reset Admin Password
```bash
# Docker
docker exec picpeak-backend node scripts/reset-admin-password.js
# Native
cd /opt/picpeak/backend
sudo -u picpeak node scripts/reset-admin-password.js
```
### Getting Help
1. **Check logs:**
- Docker: `docker compose logs -f`
- Native: `sudo journalctl -u picpeak-backend -f`
- Installation: `/tmp/picpeak-setup-*.log`
2. **Documentation:**
- [Full Documentation](https://github.com/yourusername/wedding-photo-sharing)
- [Deployment Guide](./DEPLOYMENT_GUIDE.md)
3. **Support:**
- [GitHub Issues](https://github.com/yourusername/wedding-photo-sharing/issues)
- Include: Error messages, system info (`uname -a`), installation method
## 🔒 Security Best Practices
### Essential Security
1. **Change default admin password immediately**
2. **Use HTTPS for production** (Let's Encrypt included)
3. **Configure firewall** (only open necessary ports)
4. **Regular updates** (system and PicPeak)
5. **Automated backups** (configure in admin panel)
### Advanced Security
- Use VPN for admin panel access
- Configure fail2ban for brute force protection
- Enable audit logging
- Regular security scans
- Implement IP whitelisting
## 📊 Performance Optimization
### Docker Optimization
```yaml
# Adjust in docker-compose.yml
services:
backend:
deploy:
resources:
limits:
cpus: '2'
memory: 2G
```
### Native Optimization
```bash
# Increase Node.js memory
echo "NODE_OPTIONS=--max-old-space-size=2048" >> /opt/picpeak/backend/.env
sudo systemctl restart picpeak-backend
```
## 🎯 Quick Setup Examples
### Home/Office Network
```bash
# Simple local setup without domain
sudo ./setup.sh --native --email admin@local.com
```
### Public Website with HTTPS
```bash
# Full production setup
sudo ./setup.sh --docker \
--domain photos.company.com \
--email admin@company.com \
--enable-ssl
```
### Raspberry Pi Setup
```bash
# Optimized for ARM devices
sudo ./setup.sh --native \
--port 8080 \
--email pi@local.com
```
## ✅ Post-Installation Checklist
- [ ] Admin password changed
- [ ] Email configuration tested
- [ ] First test gallery created
- [ ] Backup schedule configured
- [ ] Firewall rules applied
- [ ] SSL certificate working (if applicable)
- [ ] Monitoring setup
- [ ] Documentation bookmarked
---
**PicPeak Setup v1.0** | [Documentation](https://github.com/yourusername/wedding-photo-sharing) | [Support](https://github.com/yourusername/wedding-photo-sharing/issues)
+1184
View File
File diff suppressed because it is too large Load Diff
-276
View File
@@ -1,276 +0,0 @@
# 🚀 Simple Setup Guide
This guide helps non-technical users install PicPeak on Raspberry Pi or VPS systems using the automated setup script.
## 📋 Quick Setup
### Step 1: Download and Run Setup Script
```bash
# Download PicPeak
wget -O simple-setup.sh https://raw.githubusercontent.com/the-luap/picpeak/main/simple-setup.sh
# Make it executable
chmod +x simple-setup.sh
# Run the setup
bash simple-setup.sh
```
**Or in one command:**
```bash
curl -fsSL https://raw.githubusercontent.com/the-luap/picpeak/main/simple-setup.sh | bash
```
### Step 2: Follow the Interactive Prompts
The script will guide you through:
1. ✅ System requirements check
2. 🐋 Docker installation (if needed)
3. 🌐 Domain/IP configuration
4. 📧 Email setup (optional)
5. 🔐 Automatic password generation
6. 📦 PicPeak download and configuration
7. 🚀 Service startup
## 📋 Prerequisites
### Minimum System Requirements
- **OS**: Ubuntu, Debian, Raspberry Pi OS, CentOS, or Fedora
- **RAM**: 2GB minimum (script will warn if less)
- **Storage**: 20GB free space minimum
- **Network**: Internet connection for downloads
### Supported Systems
- ✅ Ubuntu 20.04+
- ✅ Debian 11+
- ✅ Raspberry Pi OS (32-bit and 64-bit)
- ✅ CentOS 7+
- ✅ Fedora 30+
## 🛠️ What the Script Does
### Automatic Installation
1. **System Check**: Verifies RAM, disk space, and architecture
2. **Docker Setup**: Installs Docker and Docker Compose if missing
3. **Network Configuration**: Helps configure domain/IP access
4. **Email Configuration**: Optional setup for Gmail, SendGrid, or custom SMTP
5. **Security**: Generates secure passwords automatically
6. **Service Deployment**: Downloads, builds, and starts PicPeak
7. **SSL Support**: Optional Let's Encrypt SSL setup
### Generated Configurations
- Secure JWT secret (64 hex characters)
- Database password (PostgreSQL)
- Redis password
- Environment configuration file
- Docker Compose setup
## 🌐 Network Configuration Options
### 1. Local Network Access (Default)
- Uses your local IP address
- Accessible only on your local network
- Perfect for home/office use
### 2. Public IP Access
- Uses your server's public IP
- Accessible from the internet
- Requires firewall configuration
### 3. Domain Name
- Uses your custom domain (e.g., photos.mydomain.com)
- Supports SSL/HTTPS with Let's Encrypt
- Professional setup
### 4. Development Mode
- Uses localhost
- Only accessible from the same machine
- Good for testing
## 📧 Email Configuration
### Gmail (Recommended for Beginners)
1. Enable 2-Factor Authentication on your Google account
2. Generate an App Password for PicPeak
3. Use your Gmail address and the App Password
### SendGrid (Recommended for Production)
1. Sign up at sendgrid.com (free tier: 100 emails/day)
2. Create an API key
3. Verify your sender email address
### Custom SMTP
- Use any SMTP server
- Configure host, port, security, and credentials
### Skip Email Setup
- Configure later in the admin panel
- PicPeak works without email but notifications won't be sent
## 🔐 Security Features
### Automatic Password Generation
- JWT secret: 64 hex characters
- Database password: 32 base64 characters (no $ symbols)
- Redis password: 32 base64 characters (no $ symbols)
### First Login Security
- Auto-generated admin password shown in setup
- **Mandatory password change** on first login
- Strong password requirements enforced
### SSL/HTTPS Support
- Optional Let's Encrypt integration
- Automatic nginx reverse proxy setup
- Forced HTTPS redirects
## 📍 Access Information
After successful installation, you'll get:
### Access URLs
- **Gallery**: `http://your-domain:3000` (or `https://your-domain` with SSL)
- **Admin Panel**: `http://your-domain:3001/admin` (or `https://your-domain/admin` with SSL)
### Admin Credentials
- **Email**: `admin@example.com`
- **Password**: Auto-generated (shown during setup)
## 🚨 Important First Steps
### 1. Change Admin Password
- The password change is **mandatory** for security
- You cannot skip this step
- New password requirements:
- Minimum 12 characters
- Mixed case letters
- Numbers and special characters
### 2. Configure Email (if skipped)
1. Login to admin panel
2. Go to Settings → Email Configuration
3. Enter your SMTP details
4. Test email sending
### 3. Create Your First Gallery
1. Go to Admin Panel → Events
2. Click "Create New Event"
3. Upload photos to the generated folders
4. Share the gallery URL and password
## 🛠️ Management Commands
### Service Management
```bash
# View logs
cd ~/picpeak && docker compose logs -f
# Stop services
cd ~/picpeak && docker compose down
# Start services
cd ~/picpeak && docker compose up -d
# Restart services
cd ~/picpeak && docker compose restart
```
### Admin Password Reset
```bash
# Reset admin password
docker exec picpeak-backend node scripts/show-admin-credentials.js --reset
```
### Updates
```bash
cd ~/picpeak
git pull
docker compose down
docker compose build
docker compose up -d
```
## 🐛 Troubleshooting
### Common Issues
#### "Permission denied" when running script
```bash
chmod +x simple-setup.sh
```
#### "Docker command not found" after installation
```bash
# Log out and back in, or run:
newgrp docker
```
#### Can't access web interface
1. Check if services are running: `docker compose ps`
2. Check firewall: `sudo ufw allow 3000` and `sudo ufw allow 3001`
3. Verify URLs in browser match the setup configuration
#### 502 Bad Gateway
1. Wait 60 seconds for services to fully start
2. Check backend logs: `docker compose logs backend`
3. Restart frontend: `docker restart picpeak-frontend`
#### Email not working
1. Verify SMTP settings in admin panel
2. Check email queue: Admin Panel → Settings → Email
3. Test with a simple service like Gmail first
### Getting Help
1. Check the logs: `docker compose logs -f`
2. Review this guide and the main deployment guide
3. Search existing GitHub issues
4. Create a new issue with:
- Error messages
- System information (`uname -a`)
- Setup choices made
## 📁 File Locations
After installation:
- **Installation directory**: `~/picpeak`
- **Photos**: `~/picpeak/storage/events/`
- **Logs**: `~/picpeak/logs/`
- **Database**: Docker volume `picpeak_postgres-data`
- **Backups**: `~/picpeak/backup/`
## 🔒 Security Recommendations
### For Internet-Facing Installations
1. **Use HTTPS**: Enable SSL during setup
2. **Firewall**: Only open ports 80 and 443
3. **Updates**: Keep system and Docker updated
4. **Backups**: Configure automatic backups in admin panel
5. **Monitoring**: Regularly check logs for suspicious activity
### For Local Network Use
1. **Strong passwords**: Use the auto-generated passwords
2. **Network security**: Secure your local network
3. **Regular backups**: Even for local use
4. **Access control**: Only share admin credentials with trusted users
## 🎯 Next Steps
After installation:
1. **Complete admin setup** (change password)
2. **Configure email notifications**
3. **Create your first test gallery**
4. **Set up automated backups**
5. **Customize themes and branding**
6. **Configure analytics** (optional)
## 📞 Support
- **Documentation**: Check the main deployment guide
- **Issues**: GitHub Issues page
- **Community**: GitHub Discussions
- **Updates**: Star the repository for notifications
---
**PicPeak Beta Setup Script v1.0** - Making photo sharing simple for everyone! 📸
-941
View File
@@ -1,941 +0,0 @@
#!/bin/bash
# ============================================================================
# Simple Setup Script for PicPeak
# ============================================================================
#
# This script helps non-technical users install PicPeak, a secure photo
# sharing platform for weddings and events.
#
# Supported Systems: Ubuntu, Debian, Raspberry Pi OS, CentOS, Fedora
# Requirements: At least 2GB RAM and 20GB free storage
#
# Usage: bash simple-setup.sh
# ============================================================================
set -e # Exit on any error
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
PURPLE='\033[0;35m'
NC='\033[0m' # No Color
# Configuration variables
PICPEAK_DIR="$HOME/picpeak"
DOMAIN=""
ENABLE_SSL="false"
SMTP_CONFIGURED="false"
AUTO_INSTALL="false"
# Print colored messages
print_success() { echo -e "${GREEN}$1${NC}"; }
print_error() { echo -e "${RED}$1${NC}"; }
print_warning() { echo -e "${YELLOW}⚠️ $1${NC}"; }
print_info() { echo -e "${BLUE}$1${NC}"; }
print_step() { echo -e "${PURPLE}🔄 $1${NC}"; }
# Print banner
print_banner() {
echo -e "${PURPLE}"
echo "============================================================================"
echo " ____ _ ____ _ "
echo " | _ \\(_) ___| _ \\ ___ __ _| | __"
echo " | |_) | |/ __| |_) / _ \\/ _\` | |/ /"
echo " | __/| | (__| __/ __/ (_| | < "
echo " |_| |_|\\___|_| \\___|\\__,_|_|\\_\\"
echo ""
echo " 🚀 PicPeak Setup Script v1.0"
echo " 📸 Secure Photo Sharing for Weddings & Events"
echo "============================================================================"
echo -e "${NC}"
}
# Show progress bar
show_progress() {
local current=$1
local total=$2
local width=50
local percentage=$((current * 100 / total))
local filled=$((current * width / total))
printf "\r["
printf "%*s" "$filled" | tr ' ' '▓'
printf "%*s" "$((width - filled))" | tr ' ' '░'
printf "] %d%% (%d/%d)" "$percentage" "$current" "$total"
}
# Check if command exists
command_exists() {
command -v "$1" >/dev/null 2>&1
}
# Detect operating system
detect_os() {
if [ -f /etc/os-release ]; then
. /etc/os-release
OS=$ID
OS_VERSION=$VERSION_ID
else
print_error "Cannot detect operating system"
exit 1
fi
case $OS in
"ubuntu"|"debian"|"raspbian")
PACKAGE_MANAGER="apt"
PACKAGE_UPDATE="apt update"
PACKAGE_INSTALL="apt install -y"
;;
"centos"|"rhel"|"fedora")
PACKAGE_MANAGER="yum"
if command_exists dnf; then
PACKAGE_MANAGER="dnf"
PACKAGE_UPDATE="dnf update -y"
PACKAGE_INSTALL="dnf install -y"
else
PACKAGE_UPDATE="yum update -y"
PACKAGE_INSTALL="yum install -y"
fi
;;
*)
print_warning "Operating system $OS not specifically supported, but might work"
PACKAGE_MANAGER="unknown"
;;
esac
print_info "Detected OS: $OS $OS_VERSION"
}
# Check system requirements
check_requirements() {
print_step "Checking system requirements..."
# Check RAM
local ram_gb=$(free -m | awk 'NR==2{printf "%.1f", $2/1024}')
local ram_int=$(printf "%.0f" "$ram_gb")
if [ "$ram_int" -lt 2 ]; then
print_warning "System has ${ram_gb}GB RAM. Minimum 2GB recommended."
echo -n "Continue anyway? (y/N): "
read -r continue_ram
if [[ ! "$continue_ram" =~ ^[Yy]$ ]]; then
print_error "Installation cancelled"
exit 1
fi
else
print_success "RAM: ${ram_gb}GB (OK)"
fi
# Check disk space
local disk_gb=$(df -h . | awk 'NR==2 {print $4}' | sed 's/G//')
local disk_int=$(printf "%.0f" "$disk_gb" 2>/dev/null || echo "0")
if [ "$disk_int" -lt 20 ]; then
print_warning "Available disk space: ${disk_gb}GB. Minimum 20GB recommended."
echo -n "Continue anyway? (y/N): "
read -r continue_disk
if [[ ! "$continue_disk" =~ ^[Yy]$ ]]; then
print_error "Installation cancelled"
exit 1
fi
else
print_success "Disk space: ${disk_gb}GB available (OK)"
fi
# Check architecture (warn for 32-bit)
local arch=$(uname -m)
if [[ "$arch" == "armv7l" || "$arch" == "i386" || "$arch" == "i686" ]]; then
print_warning "32-bit architecture detected ($arch). PicPeak works better on 64-bit systems."
else
print_success "Architecture: $arch (OK)"
fi
}
# Install Docker
install_docker() {
if command_exists docker; then
print_success "Docker is already installed"
return 0
fi
print_step "Installing Docker..."
case $PACKAGE_MANAGER in
"apt")
# Remove old versions
sudo apt remove -y docker docker-engine docker.io containerd runc 2>/dev/null || true
# Update and install prerequisites
sudo $PACKAGE_UPDATE
sudo $PACKAGE_INSTALL ca-certificates curl gnupg lsb-release
# Add Docker's official GPG key
sudo mkdir -p /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/$OS/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
# Add Docker repository
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/$OS $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
# Install Docker
sudo apt update
sudo $PACKAGE_INSTALL docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
;;
"yum"|"dnf")
sudo $PACKAGE_INSTALL yum-utils
sudo yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo
sudo $PACKAGE_INSTALL docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
;;
*)
print_error "Automatic Docker installation not supported for your OS"
print_info "Please install Docker manually and run this script again"
exit 1
;;
esac
# Start and enable Docker
sudo systemctl start docker
sudo systemctl enable docker
# Add current user to docker group
sudo usermod -aG docker $USER
print_success "Docker installed successfully"
print_warning "You may need to log out and back in for Docker permissions to take effect"
}
# Install Docker Compose (if not included with Docker)
install_docker_compose() {
if command_exists docker-compose || docker compose version >/dev/null 2>&1; then
print_success "Docker Compose is available"
return 0
fi
print_step "Installing Docker Compose..."
# Try to install via package manager first
case $PACKAGE_MANAGER in
"apt")
sudo $PACKAGE_INSTALL docker-compose-plugin
;;
"yum"|"dnf")
sudo $PACKAGE_INSTALL docker-compose-plugin
;;
esac
# If that didn't work, install standalone version
if ! command_exists docker-compose && ! docker compose version >/dev/null 2>&1; then
local compose_version="v2.20.2"
local arch=$(uname -m)
# Map architecture names
case $arch in
"x86_64") arch="x86_64" ;;
"aarch64") arch="aarch64" ;;
"armv7l") arch="armv7" ;;
*)
print_error "Unsupported architecture for Docker Compose: $arch"
exit 1
;;
esac
sudo curl -L "https://github.com/docker/compose/releases/download/$compose_version/docker-compose-linux-$arch" -o /usr/local/bin/docker-compose
sudo chmod +x /usr/local/bin/docker-compose
fi
print_success "Docker Compose installed successfully"
}
# Get domain/IP configuration
configure_domain() {
print_step "Configuring domain and network settings..."
echo ""
# Auto-detect IP address
local public_ip=""
local local_ip=""
# Try to get public IP
public_ip=$(curl -s ifconfig.me 2>/dev/null || curl -s ipinfo.io/ip 2>/dev/null || echo "")
# Get local IP
local_ip=$(hostname -I | awk '{print $1}' 2>/dev/null || ip route get 1 | awk '{print $NF;exit}' 2>/dev/null || echo "")
echo "Network configuration options:"
echo ""
echo "1. Use local IP address (LAN only): $local_ip"
if [ -n "$public_ip" ]; then
echo "2. Use public IP address (internet): $public_ip"
echo "3. Use custom domain name (e.g., photos.mydomain.com)"
echo "4. Use localhost (development only)"
else
echo "2. Use custom domain name (e.g., photos.mydomain.com)"
echo "3. Use localhost (development only)"
fi
echo ""
echo -n "Choose option [1]: "
read -r network_choice
case $network_choice in
"2")
if [ -n "$public_ip" ]; then
DOMAIN="$public_ip"
else
echo -n "Enter your domain name: "
read -r DOMAIN
fi
;;
"3")
if [ -n "$public_ip" ]; then
echo -n "Enter your domain name: "
read -r DOMAIN
else
DOMAIN="localhost"
fi
;;
"4")
if [ -n "$public_ip" ]; then
DOMAIN="localhost"
else
echo -n "Enter your domain name: "
read -r DOMAIN
fi
;;
*)
DOMAIN="$local_ip"
;;
esac
print_info "Domain/IP set to: $DOMAIN"
# Ask about SSL
if [[ "$DOMAIN" != "localhost" && "$DOMAIN" != "127.0.0.1" && ! "$DOMAIN" =~ ^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
echo ""
echo "Do you want to enable SSL/HTTPS with Let's Encrypt?"
echo "(This requires a valid domain name pointing to this server)"
echo -n "Enable SSL? (y/N): "
read -r enable_ssl
if [[ "$enable_ssl" =~ ^[Yy]$ ]]; then
ENABLE_SSL="true"
fi
fi
}
# Configure email settings
configure_email() {
print_step "Configuring email settings..."
echo ""
echo "PicPeak can send notification emails for:"
echo "• New gallery creation"
echo "• Expiration warnings"
echo "• Password resets"
echo ""
echo "Email providers:"
echo "1. Gmail (free, requires app password)"
echo "2. SendGrid (free tier available)"
echo "3. Custom SMTP server"
echo "4. Skip email setup (can configure later)"
echo ""
echo -n "Choose option [4]: "
read -r email_choice
case $email_choice in
"1")
configure_gmail
;;
"2")
configure_sendgrid
;;
"3")
configure_custom_smtp
;;
*)
print_info "Email setup skipped - you can configure this later in the admin panel"
;;
esac
}
# Configure Gmail
configure_gmail() {
echo ""
echo "Gmail Configuration:"
echo "1. Go to your Google Account settings"
echo "2. Enable 2-Factor Authentication"
echo "3. Generate an App Password for PicPeak"
echo "4. Use that App Password (not your regular password)"
echo ""
echo -n "Enter your Gmail address: "
read -r gmail_user
echo -n "Enter your Gmail App Password: "
read -s gmail_pass
echo ""
# Set SMTP variables
SMTP_HOST="smtp.gmail.com"
SMTP_PORT="587"
SMTP_SECURE="false"
SMTP_USER="$gmail_user"
SMTP_PASS="$gmail_pass"
EMAIL_FROM="$gmail_user"
SMTP_CONFIGURED="true"
print_success "Gmail configured successfully"
}
# Configure SendGrid
configure_sendgrid() {
echo ""
echo "SendGrid Configuration:"
echo "1. Sign up at sendgrid.com (free tier: 100 emails/day)"
echo "2. Create an API key in Settings > API Keys"
echo "3. Verify your sender email address"
echo ""
echo -n "Enter your SendGrid API key: "
read -s sendgrid_key
echo ""
echo -n "Enter your verified sender email: "
read -r sender_email
# Set SMTP variables
SMTP_HOST="smtp.sendgrid.net"
SMTP_PORT="587"
SMTP_SECURE="false"
SMTP_USER="apikey"
SMTP_PASS="$sendgrid_key"
EMAIL_FROM="$sender_email"
SMTP_CONFIGURED="true"
print_success "SendGrid configured successfully"
}
# Configure custom SMTP
configure_custom_smtp() {
echo ""
echo "Custom SMTP Configuration:"
echo -n "SMTP Host: "
read -r SMTP_HOST
echo -n "SMTP Port (usually 587 or 465): "
read -r SMTP_PORT
echo -n "Use SSL/TLS? (y/N): "
read -r smtp_secure
if [[ "$smtp_secure" =~ ^[Yy]$ ]]; then
SMTP_SECURE="true"
else
SMTP_SECURE="false"
fi
echo -n "SMTP Username: "
read -r SMTP_USER
echo -n "SMTP Password: "
read -s SMTP_PASS
echo ""
echo -n "From email address: "
read -r EMAIL_FROM
SMTP_CONFIGURED="true"
print_success "Custom SMTP configured successfully"
}
# Generate secure passwords
generate_passwords() {
print_step "Generating secure passwords..."
# Generate JWT secret (64 hex characters)
JWT_SECRET=$(openssl rand -hex 32 2>/dev/null || head -c 32 /dev/urandom | od -A n -t x1 | tr -d ' \n')
# Generate database password (avoid $ character for Docker Compose)
DB_PASSWORD=$(openssl rand -base64 32 2>/dev/null | tr -d '$' || head -c 24 /dev/urandom | base64 | tr -d '$')
# Generate Redis password (avoid $ character for Docker Compose)
REDIS_PASSWORD=$(openssl rand -base64 32 2>/dev/null | tr -d '$' || head -c 24 /dev/urandom | base64 | tr -d '$')
print_success "Secure passwords generated"
}
# Download PicPeak
download_picpeak() {
print_step "Downloading PicPeak..."
if [ -d "$PICPEAK_DIR" ]; then
print_warning "Directory $PICPEAK_DIR already exists"
echo -n "Remove existing directory and download fresh copy? (y/N): "
read -r remove_existing
if [[ "$remove_existing" =~ ^[Yy]$ ]]; then
rm -rf "$PICPEAK_DIR"
else
print_error "Installation cancelled"
exit 1
fi
fi
# Try git clone first, fall back to wget if git is not available
if command_exists git; then
git clone https://github.com/the-luap/picpeak.git "$PICPEAK_DIR"
else
print_warning "Git not found, downloading archive..."
mkdir -p "$PICPEAK_DIR"
cd "$PICPEAK_DIR"
if command_exists wget; then
wget -O picpeak.tar.gz "https://github.com/the-luap/picpeak/archive/refs/heads/main.tar.gz"
elif command_exists curl; then
curl -L -o picpeak.tar.gz "https://github.com/the-luap/picpeak/archive/refs/heads/main.tar.gz"
else
print_error "Neither git, wget, nor curl found. Please install one of these tools."
exit 1
fi
tar -xzf picpeak.tar.gz --strip-components=1
rm picpeak.tar.gz
fi
print_success "PicPeak downloaded to $PICPEAK_DIR"
}
# Create directories and set permissions
setup_directories() {
print_step "Setting up directories..."
cd "$PICPEAK_DIR"
# Create required directories
mkdir -p events/active events/archived data logs backup storage/temp storage/thumbnails storage/uploads
# Set proper permissions
chmod -R 755 events data logs backup storage
print_success "Directories created and permissions set"
}
# Create environment file
create_env_file() {
print_step "Creating environment configuration..."
cd "$PICPEAK_DIR"
# Determine protocol and port
local protocol="http"
local frontend_port="3000"
local backend_port="3001"
if [ "$ENABLE_SSL" = "true" ]; then
protocol="https"
frontend_port="443"
backend_port="443"
fi
# Create .env file
cat > .env << EOF
# PicPeak Configuration - Generated by setup script
# Generated on: $(date)
# Node Environment
NODE_ENV=production
# Database Configuration
DB_USER=picpeak
DB_PASSWORD=$DB_PASSWORD
DB_NAME=picpeak_prod
# Redis Configuration
REDIS_PASSWORD=$REDIS_PASSWORD
# Security
JWT_SECRET=$JWT_SECRET
# Network Configuration
FRONTEND_URL=${protocol}://${DOMAIN}$([ "$frontend_port" != "80" ] && [ "$frontend_port" != "443" ] && echo ":$frontend_port" || echo "")
ADMIN_URL=${protocol}://${DOMAIN}$([ "$backend_port" != "80" ] && [ "$backend_port" != "443" ] && echo ":$backend_port" || echo "")
VITE_API_URL=${protocol}://${DOMAIN}$([ "$backend_port" != "80" ] && [ "$backend_port" != "443" ] && echo ":$backend_port" || echo "")/api
# Port Configuration (for direct access without reverse proxy)
FRONTEND_PORT=$frontend_port
BACKEND_PORT=$backend_port
# Admin Configuration
ADMIN_USERNAME=admin
ADMIN_EMAIL=admin@example.com
# Timezone
TZ=$(timedatectl show --property=Timezone --value 2>/dev/null || echo "UTC")
EOF
# Add email configuration if configured
if [ "$SMTP_CONFIGURED" = "true" ]; then
cat >> .env << EOF
# Email Configuration
SMTP_HOST=$SMTP_HOST
SMTP_PORT=$SMTP_PORT
SMTP_SECURE=$SMTP_SECURE
SMTP_USER=$SMTP_USER
SMTP_PASS=$SMTP_PASS
EMAIL_FROM=$EMAIL_FROM
EOF
else
cat >> .env << EOF
# Email Configuration (disabled - configure in admin panel)
# SMTP_HOST=smtp.gmail.com
# SMTP_PORT=587
# SMTP_SECURE=false
# SMTP_USER=your-email@gmail.com
# SMTP_PASS=your-app-password
# EMAIL_FROM=noreply@yourdomain.com
EOF
fi
# Set secure permissions
chmod 600 .env
print_success "Environment file created"
}
# Start PicPeak services
start_services() {
print_step "Starting PicPeak services..."
cd "$PICPEAK_DIR"
# Create docker-compose override for port configuration if needed
if [ "$ENABLE_SSL" = "false" ]; then
cat > docker-compose.override.yml << EOF
version: '3.8'
services:
frontend:
ports:
- "${FRONTEND_PORT:-3000}:80"
backend:
ports:
- "${BACKEND_PORT:-3001}:3001"
EOF
fi
# Start services
echo ""
print_info "Building and starting Docker containers..."
echo "This may take several minutes on first run..."
echo ""
# Use docker compose if available, fallback to docker-compose
if docker compose version >/dev/null 2>&1; then
docker compose up -d --build
else
docker-compose up -d --build
fi
print_success "Services started successfully"
}
# Wait for services to be ready
wait_for_services() {
print_step "Waiting for services to be ready..."
local max_attempts=60
local attempt=0
local backend_ready=false
echo ""
while [ $attempt -lt $max_attempts ] && [ "$backend_ready" = false ]; do
attempt=$((attempt + 1))
show_progress $attempt $max_attempts
# Check if backend is responding
if curl -s "http://localhost:${BACKEND_PORT:-3001}/api/health" >/dev/null 2>&1; then
backend_ready=true
else
sleep 2
fi
done
echo ""
if [ "$backend_ready" = true ]; then
print_success "Services are ready!"
else
print_warning "Services are starting but may need more time"
print_info "You can check status with: docker compose logs -f"
fi
}
# Get admin credentials
get_admin_credentials() {
print_step "Retrieving admin credentials..."
cd "$PICPEAK_DIR"
local max_attempts=30
local attempt=0
local admin_password=""
# Wait for admin password to be generated
while [ $attempt -lt $max_attempts ] && [ -z "$admin_password" ]; do
attempt=$((attempt + 1))
# Try to get password from logs
if docker compose logs backend 2>/dev/null | grep -o "Admin password generated: [A-Za-z0-9!@#$%^&*]*" | tail -1 | cut -d' ' -f4 >/dev/null 2>&1; then
admin_password=$(docker compose logs backend 2>/dev/null | grep -o "Admin password generated: [A-Za-z0-9!@#$%^&*]*" | tail -1 | cut -d' ' -f4)
break
fi
# Try to get password from credentials file
if docker exec picpeak-backend cat data/ADMIN_CREDENTIALS.txt 2>/dev/null | grep "Password:" | cut -d' ' -f2 >/dev/null 2>&1; then
admin_password=$(docker exec picpeak-backend cat data/ADMIN_CREDENTIALS.txt 2>/dev/null | grep "Password:" | cut -d' ' -f2)
break
fi
sleep 2
done
if [ -n "$admin_password" ]; then
ADMIN_PASSWORD="$admin_password"
print_success "Admin credentials retrieved"
else
print_warning "Could not automatically retrieve admin password"
print_info "You can get it manually with: docker compose logs backend | grep 'Admin password'"
fi
}
# Install reverse proxy (nginx) for SSL
setup_ssl() {
if [ "$ENABLE_SSL" = "false" ]; then
return 0
fi
print_step "Setting up SSL with nginx and Let's Encrypt..."
# Install nginx and certbot
case $PACKAGE_MANAGER in
"apt")
sudo $PACKAGE_UPDATE
sudo $PACKAGE_INSTALL nginx certbot python3-certbot-nginx
;;
"yum"|"dnf")
sudo $PACKAGE_INSTALL nginx certbot python3-certbot-nginx
;;
*)
print_warning "Automatic nginx/certbot installation not supported for your OS"
print_info "Please install nginx and certbot manually"
return 1
;;
esac
# Create nginx configuration
sudo tee /etc/nginx/sites-available/picpeak > /dev/null << EOF
server {
listen 80;
server_name $DOMAIN;
# Temporary configuration - certbot will modify this
location / {
proxy_pass http://localhost:3000;
proxy_set_header Host \$host;
proxy_set_header X-Real-IP \$remote_addr;
proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto \$scheme;
}
location /api {
proxy_pass http://localhost:3001;
proxy_set_header Host \$host;
proxy_set_header X-Real-IP \$remote_addr;
proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto \$scheme;
}
location ~ ^/(photos|thumbnails|uploads|admin) {
proxy_pass http://localhost:3001;
proxy_set_header Host \$host;
proxy_set_header X-Real-IP \$remote_addr;
proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto \$scheme;
}
}
EOF
# Enable site
sudo ln -sf /etc/nginx/sites-available/picpeak /etc/nginx/sites-enabled/
sudo rm -f /etc/nginx/sites-enabled/default
# Test nginx configuration
sudo nginx -t
sudo systemctl restart nginx
sudo systemctl enable nginx
# Get SSL certificate
print_info "Obtaining SSL certificate from Let's Encrypt..."
sudo certbot --nginx -d "$DOMAIN" --non-interactive --agree-tos --email "admin@$DOMAIN" --redirect
# Update .env for HTTPS URLs
sed -i "s|http://|https://|g" "$PICPEAK_DIR/.env"
sed -i "s|:3000||g" "$PICPEAK_DIR/.env"
sed -i "s|:3001||g" "$PICPEAK_DIR/.env"
print_success "SSL certificate installed and configured"
}
# Display final information
show_completion_info() {
echo ""
echo -e "${GREEN}============================================================================"
echo " 🎉 PicPeak Installation Complete!"
echo "============================================================================${NC}"
echo ""
# Determine access URLs
local protocol="http"
local port_suffix=""
if [ "$ENABLE_SSL" = "true" ]; then
protocol="https"
else
port_suffix=":3000"
fi
local gallery_url="${protocol}://${DOMAIN}${port_suffix}"
local admin_url="${protocol}://${DOMAIN}${port_suffix}/admin"
if [ "$ENABLE_SSL" = "false" ]; then
admin_url="${protocol}://${DOMAIN}:3001/admin"
fi
echo -e "${BLUE}📍 Access URLs:${NC}"
echo " Gallery: $gallery_url"
echo " Admin Panel: $admin_url"
echo ""
echo -e "${YELLOW}🔐 Admin Login Credentials:${NC}"
echo " Email: admin@example.com"
if [ -n "$ADMIN_PASSWORD" ]; then
echo " Password: $ADMIN_PASSWORD"
else
echo " Password: (check logs with: docker compose logs backend | grep 'Admin password')"
fi
echo ""
echo -e "${RED}⚠️ IMPORTANT SECURITY NOTES:${NC}"
echo " • You MUST change the admin password on first login"
echo " • The password change is mandatory for security"
echo " • Save your new password securely"
if [ "$ENABLE_SSL" = "false" ]; then
echo " • Consider enabling HTTPS for production use"
fi
echo ""
echo -e "${PURPLE}📖 Quick Start Guide:${NC}"
echo " 1. Visit the admin panel: $admin_url"
echo " 2. Login with the credentials above"
echo " 3. Complete the mandatory password change"
echo " 4. Configure email settings (if not done during setup)"
echo " 5. Create your first photo gallery!"
echo ""
echo -e "${BLUE}🛠️ Useful Commands:${NC}"
echo " View logs: cd $PICPEAK_DIR && docker compose logs -f"
echo " Stop services: cd $PICPEAK_DIR && docker compose down"
echo " Start services: cd $PICPEAK_DIR && docker compose up -d"
echo " Reset admin password: docker exec picpeak-backend node scripts/show-admin-credentials.js --reset"
echo ""
if [ "$SMTP_CONFIGURED" = "false" ]; then
echo -e "${YELLOW}📧 Email Configuration:${NC}"
echo " Email notifications are not configured. You can set them up in:"
echo " Admin Panel → Settings → Email Configuration"
echo ""
fi
echo -e "${GREEN}Installation directory: $PICPEAK_DIR${NC}"
echo -e "${GREEN}For support and documentation: https://github.com/the-luap/picpeak${NC}"
echo ""
echo -e "${PURPLE}============================================================================${NC}"
}
# Handle interruption
cleanup() {
echo ""
print_warning "Installation interrupted"
exit 130
}
# Main installation function
main() {
# Handle Ctrl+C gracefully
trap cleanup INT
print_banner
echo "This script will install PicPeak on your system."
echo "It will automatically:"
echo "• Check system requirements"
echo "• Install Docker and Docker Compose"
echo "• Download and configure PicPeak"
echo "• Generate secure passwords"
echo "• Start all services"
echo ""
echo -n "Continue with installation? (Y/n): "
read -r continue_install
if [[ "$continue_install" =~ ^[Nn]$ ]]; then
print_info "Installation cancelled by user"
exit 0
fi
echo ""
print_info "Starting PicPeak installation..."
echo ""
# Installation steps
detect_os
check_requirements
# Check for Docker
if ! command_exists docker; then
echo -n "Docker is not installed. Install it automatically? (Y/n): "
read -r install_docker_prompt
if [[ ! "$install_docker_prompt" =~ ^[Nn]$ ]]; then
install_docker
else
print_error "Docker is required for PicPeak. Please install it manually."
exit 1
fi
fi
install_docker_compose
configure_domain
configure_email
generate_passwords
download_picpeak
setup_directories
create_env_file
if [ "$ENABLE_SSL" = "true" ]; then
setup_ssl
fi
start_services
wait_for_services
get_admin_credentials
show_completion_info
print_success "PicPeak is now running!"
}
# Run main function
main "$@"