Compare commits

..

635 Commits

Author SHA1 Message Date
diced 0b1db04159 fix: add errors to spec 2026-03-03 16:29:24 -08:00
diced 4735b102c3 fix: settings errors 2026-03-03 15:22:23 -08:00
diced 5d48735dfb fix: lint 2026-03-02 22:45:31 -08:00
diced ea9599a67a fix: more 2026-03-02 22:43:47 -08:00
diced 9bd22bd574 fix: responses + add descriptions 2026-03-02 22:43:40 -08:00
diced 6fef46246e refactor: generalized error codes 2026-03-02 19:57:23 -08:00
diced 3f159b3509 fix: finish up api refactor 2026-03-02 14:29:41 -08:00
diced eb3a58e790 feat: descriptions for api routes 2026-03-02 00:25:37 -08:00
diced 454b40501a refactor: models to zod 2026-03-01 22:41:36 -08:00
diced 4c6679b568 feat: add response schemas (WIP, hella unstable!!) 2026-03-01 14:57:16 -08:00
diced 3c757374e1 feat: revamp option selection for files page 2026-02-26 16:53:31 -08:00
diced c0e1aa9ac6 feat: revamp folders page 2026-02-26 16:11:49 -08:00
diced 40fd0b19eb feat: add multiple files for text uploads 2026-02-24 02:14:03 -08:00
diced 41240b7aff refactor: upload/partial logic + more sanitzation 2026-02-23 22:04:50 -08:00
diced 01f177fbc3 fix: permissions on docker scripts 2026-02-23 00:43:41 -08:00
diced ab1d394a46 fix: permissions 2026-02-23 00:42:01 -08:00
diced d08f1ba5da fix: #1002 2026-02-23 00:20:36 -08:00
diced 641a7c9b7b fix: maybe fix oauth issues #1001 2026-02-23 00:18:26 -08:00
diced a467ffe861 feat: new notifs position 2026-02-23 00:18:17 -08:00
dicedtomato 33ff667990 Merge commit from fork 2026-02-20 21:48:01 -08:00
diced e96015f5e0 fix: refactor + perf 2026-02-19 22:38:54 -08:00
diced d4d1cdc885 feat: revamped sessions 2026-02-15 21:18:02 -08:00
diced a7d831934d fix: add http but https warning 2026-02-12 16:30:56 -08:00
diced e9ef6a2d40 fix: #983 2026-02-12 16:05:28 -08:00
diced 7520efa835 fix: use exponential moving average for estimation (#996) 2026-02-12 15:55:22 -08:00
diced cff8454ac7 fix: no schema for settings api (from #990) 2026-02-12 14:53:38 -08:00
diced 847779601a fix: dev 2026-02-12 14:53:31 -08:00
Andrew Simonson 49c2088ea3 fix: max interval checks (#990)
* introduce max interval checks

* Update validate.ts

* Update validate.ts

* Update validate.ts

* Update validate.ts
2026-02-12 14:45:50 -08:00
diced 78600103af feat(v4.4.2): version 2026-02-10 20:49:14 -08:00
diced ce8b3ed36d fix: #985 2026-02-10 20:43:33 -08:00
diced 67641c2116 fix: proper length checks for login/register (#987) 2026-02-10 20:41:43 -08:00
diced acbbb7d40a feat: add docker scripts (ENTRYPOINT) + ziplinectl 2026-02-05 16:32:31 -08:00
Huang Cheng Ting 1f672cda3a fix: view route title & handle unicode characters in raw route (#980)
* fix: prioritize file original name in view route title

* fix: update Content-Disposition header to support unicode characters
fix: display issue with raw route when text contains unicode characters

---------

Co-authored-by: dicedtomato <35403473+diced@users.noreply.github.com>
2026-02-05 16:05:49 -08:00
Huang Cheng Ting 2332d529e0 fix: prevent random character conflicts in uploads and urls (#978)
Co-authored-by: dicedtomato <35403473+diced@users.noreply.github.com>
2026-02-05 16:03:08 -08:00
diced e910fe9da5 fix: folder issues 2026-02-05 15:59:35 -08:00
diced 4656599bb0 fix: use tmpdir() for initial 2026-02-05 15:59:21 -08:00
diced d6c33b6123 fix: clean up #961 2026-02-05 15:34:45 -08:00
Christoph Schlaepfer defcc7950d feat: nested folders (#961)
* Added nested folders feature

* Fixed Linting

* Fixed Linting

* Fixed Linting

* Fixed linting

* Fixed import

* Fixed dashboard view

* Fixed dashboard view

* Added DB Migration

* Fixed dropdown selection

* Added structured dropdowns to file dialog

* Fixes Nested Folder depth lookup & Breadcrumbs

* Fixes Nested Folder dropdowns

* Linting

* Fixes Export Filename

* Fixes export hierarchy

* Implemented Reviewed Feedback, improved code

* Removed more comments
2026-02-05 14:48:44 -08:00
diced 3d55ce0def fix: change buttons + add buttons 2026-02-04 14:29:25 -08:00
diced 8c9df5af5d feat: add domain selection to urls (#977) 2026-02-03 17:48:03 -08:00
diced 5c33ae134a fix: bunch of validation fixes 2026-02-02 21:13:58 -08:00
diced b628489330 fix: build errors 2026-01-31 18:36:34 -08:00
diced e9a6e31d4f fix: add passkey warning for old passkeys 2026-01-31 18:21:22 -08:00
diced ebe37cf7c1 fix: add warning when accessing over secure (login) 2026-01-31 18:11:29 -08:00
diced 529708110b fix: metrics validations 2026-01-31 15:01:49 -08:00
diced 9066dd37fb feat: remove built-in SSL 2026-01-31 14:16:51 -08:00
diced 45848925f4 fix: validation issues edit user 2026-01-28 16:29:54 -08:00
diced 2ba1da1671 fix: don't log db 2026-01-22 15:08:38 -08:00
diced 35c7d6b70c fix: #964 2026-01-22 15:03:23 -08:00
diced f45d1b770f fix: #966 2026-01-22 15:01:32 -08:00
diced 3650178ab3 fix: #968 2026-01-22 14:21:38 -08:00
diced 574bd9114c feat(v4.4.1): version 2026-01-19 17:10:21 -08:00
Radon Rosborough 73c46b875d fix: missing input field names (#963)
* fix: missing input field names

* Use enhanceGetInputProps instead
2026-01-19 17:04:49 -08:00
diced e21670f292 fix: titles on view pages 2026-01-17 22:23:43 -08:00
Radon Rosborough 09b3ef4e26 fix: typo in docker-compose.yml (#962) 2026-01-17 22:00:26 -08:00
diced afdee6994e fix: build errors 2026-01-13 22:53:24 -08:00
diced 6f6879c58a fix: #954 2026-01-13 22:50:30 -08:00
diced 66a2f760cf fix: #957 2026-01-13 22:47:40 -08:00
diced fb3199a9d5 fix: improve on validation 2026-01-13 22:13:09 -08:00
diced 274a84397a fix: workflow 2026-01-10 23:46:16 -08:00
diced 4b585d8634 feat: gen-openapi workflow 2026-01-10 23:43:45 -08:00
diced 260c283872 feat: input validation schemas (very wip) 2026-01-10 23:32:59 -08:00
diced 4d978c11b1 fix: regen random-values 2026-01-10 15:13:38 -08:00
diced 8bdd9e8315 fix: settings -> domains logic 2026-01-09 21:22:21 -08:00
diced d4a3e877d2 fix: #956 2026-01-09 20:31:44 -08:00
dicedtomato db3c5f48a5 Merge commit from fork
* fix: passkey impl

* fix: passkey impl

* fix: passkey impl + other stuff

* fix: cookies

* fix: passkey auth w/ cookie

* fix: cookie options
2026-01-08 23:26:16 -08:00
diced cdcaa926fe fix: use gcm 2026-01-06 15:20:01 -08:00
dicedtomato 01503968ab Merge commit from fork 2026-01-06 15:11:43 -08:00
dicedtomato 8aa5ec6917 Merge commit from fork 2026-01-06 14:51:43 -08:00
dicedtomato 9befcaaf80 Merge commit from fork 2026-01-06 14:41:27 -08:00
dicedtomato bfc0e4d40c Merge commit from fork 2026-01-06 14:30:41 -08:00
diced 4fb21f678e fix: add debugs for event emitter warnings 2026-01-02 23:26:54 -08:00
diced f49598c760 fix: #950 2026-01-02 21:46:15 -08:00
diced bfd6a8769d fix: #948 and tags/folders
fixes inconsistencies when editing other user's files
- tags menu shows their tags
- folders menu shows their folders
by design, you can't and will not be able to add another user's file to
your own folder.
this also introduces a few wip stuff, might be buggy, please bear with
me!
2025-12-31 00:03:55 -08:00
diced 87cf4916a5 fix: #949 2025-12-30 23:23:04 -08:00
diced 12ea806f0a fix: #945 2025-12-30 23:15:16 -08:00
diced 6269b457d8 fix: don't clamp lines on /view 2025-12-26 16:15:23 -08:00
diced 78f5875464 fix: omit meta tags when embed is disable 2025-12-26 15:55:05 -08:00
diced 05df685bd1 fix: better max/default expiration validation 2025-12-26 15:52:31 -08:00
diced eaf245a4c9 fix: compression-type errors when no compression-percent 2025-12-26 15:43:00 -08:00
Zarox28 8a7b401b6e feat: add maximum expiration value (#934)
* feat: add maximum expiration value

* fix(settings)

* fix: add missing migration

* Update src/lib/import/version3/validateExport.ts

* fix: x-zipline-deletes-at with maxExpiration config

---------

Co-authored-by: dicedtomato <35403473+diced@users.noreply.github.com>
2025-12-18 01:50:34 -08:00
diced bb13e44bc9 fix: PWA and favicons #938 2025-12-17 19:54:23 -08:00
diced 2c21e119c4 fix: #926 2025-12-17 00:06:55 -08:00
diced 1585287b63 feat(v4.4.0): version 2025-12-13 14:37:48 -08:00
dicedtomato 1d4c3f26b4 Merge commit from fork 2025-12-13 14:31:55 -08:00
diced 589f06b460 feat: new actions page + finish impl v4 export 2025-12-08 23:30:04 -08:00
diced ca09b1319d chore: update packages + eslint + lint 2025-12-08 01:29:12 -08:00
diced 5d27c14b77 feat: import v4 jsons (settings wip) 2025-12-08 01:07:15 -08:00
diced 9da74054ff fix: #931 2025-12-07 21:58:56 -08:00
diced 7572f7f3da fix: #935 2025-12-07 20:36:12 -08:00
diced ef979d8853 feat: import v4 details (wip still) 2025-12-06 21:56:32 -08:00
diced d090ed2cc1 fix: #926 for good 2025-12-06 20:37:55 -08:00
diced 3fc8b044bb fix: #926 animated compression removes animation 2025-12-05 19:56:05 -08:00
diced 61af46f136 feat: export and import v4 (wip) (needs testing) 2025-11-19 00:22:51 -08:00
diced 771aa67673 fix: editing files that are owned by the current user again 2025-11-18 20:37:51 -08:00
diced b2db0c15a3 fix: editing files that are owned by current user 2025-11-15 23:20:11 -08:00
diced d49afe60c8 fix: #924 2025-11-14 23:52:10 -08:00
diced 3370d4b663 fix: remove random logs 2025-11-14 23:50:35 -08:00
diced 1f1bcd3a47 feat: export folder as zip file 2025-11-14 23:48:50 -08:00
diced d9df04bac5 fix: transactions not working for current user 2025-11-14 23:36:03 -08:00
diced 2bf2809269 fix: metrics erroring with null usernames 2025-11-14 23:18:01 -08:00
diced 9bb9e7e399 feat: add copy raw file link button to file modal 2025-11-14 23:08:05 -08:00
diced 89d6b2908d fix: change memory monitor to csv-like 2025-11-11 22:17:46 -08:00
diced 63c268cd1e fix: actually write new buffer to file (gps removal) 2025-11-07 22:06:29 -08:00
diced 6e2da52f77 feat: actions when viewing other user files (#918) 2025-11-03 16:37:12 -08:00
diced 04b27a2dee fix: build error 2025-11-03 15:40:15 -08:00
diced 6f4c3271c1 fix: #914 2025-11-03 15:36:09 -08:00
diced b014f10240 fix: #916 2025-11-03 15:36:03 -08:00
diced d3a417aff0 fix: #921 2025-11-03 15:24:14 -08:00
diced 63596d983e fix: #919 2025-10-28 12:10:06 -07:00
diced ffbad41994 fix: export issues (#915) 2025-10-27 15:05:01 -07:00
diced 2a6f1f418a feat: log memory usage with DEBUG_MEMORY_LOG 2025-10-27 15:01:19 -07:00
diced 2402c6f0ef fix: performance issues with code renderer (#911) 2025-10-23 21:51:37 -07:00
diced 317e97e3a6 fix: show original name in view route #908 2025-10-19 21:27:06 -07:00
Venipa f7753ccf2e fix: partial s3 upload ignoring subdirectory (#910, #909) 2025-10-18 20:56:59 -07:00
diced 2ad10e9a52 feat(v4.3.2): version 2025-10-16 21:12:40 -07:00
diced b4be96c7a8 feat: support separate db vars + file version 2025-10-16 21:02:17 -07:00
diced 69dfad201b feat: reorder/disable/enable table fields in file table 2025-10-12 21:43:50 -07:00
diced ee1681497e feat: allow any env to be read from a file 2025-10-12 21:43:34 -07:00
diced 2f19140085 feat: add file name in upload response 2025-10-03 21:01:18 -07:00
diced c9d492f9d2 feat: trust proxies option (#879) 2025-10-03 20:55:35 -07:00
diced a7a23f3fd9 chore: downgrade aws sdks (#888)
newer AWS sdks introduce dumb AWS specific stuff that break
interoperability with other services.
2025-09-19 20:26:20 -07:00
diced 36ffb669b2 fix: accidental force push lmaoo (#886)
PR: #886
2025-09-18 12:41:22 -07:00
diced f0ee4cdab3 fix: allow any host on dev 2025-09-18 12:31:59 -07:00
diced ac41dab2b2 fix: title not updating on first-load 2025-09-09 16:19:54 -07:00
diced 26661f7a83 fix: encode id for view route 2025-09-09 16:06:27 -07:00
diced 01a73df7f3 fix: say "try again" for invites when ratelimited 2025-09-08 23:08:29 -07:00
diced 6b1304f37b fix: #885 2025-09-08 23:06:27 -07:00
diced 19fc87818c feat(v4.3.1): version 2025-09-08 15:23:54 -07:00
diced f168fa676d fix: better dev scripts runner 2025-09-08 11:53:45 -07:00
diced 44cb10acf2 fix: scripts 2025-09-08 11:50:45 -07:00
diced 2c21101e9e fix: remove log 2025-09-08 11:04:54 -07:00
diced ecb83d96e3 fix: add /r/:id redirect (#882) 2025-09-08 10:35:21 -07:00
diced bfae105e5f fix: invites not working 2025-09-06 16:29:24 -07:00
diced 3240e19710 fix: bypass local login #878 2025-09-06 12:51:46 -07:00
diced 40c12ca3f0 fix: 🖕prisma (rollback to working stuff) 2025-09-06 12:37:32 -07:00
diced 4907f4e450 fix: #876 2025-09-05 20:59:22 -07:00
diced e2e3edd208 feat(v4.3.0): version 2025-09-05 11:30:53 -07:00
diced b6abfe1ca7 fix: handle thumbnails properly in raw api routes 2025-09-05 11:24:58 -07:00
diced ac61964c37 fix: new view counting method 2025-09-05 00:23:14 -07:00
diced 1924c22e1b feat: better max-views handling (#874) 2025-09-04 22:53:20 -07:00
diced c15bf27b8a fix: config path conversion 2025-09-03 11:59:43 -07:00
diced da8edb9c5d fix: prisma migrate 2025-09-03 11:49:13 -07:00
diced c5ecd6fe64 fix: once and for all fix dockerfile 2025-09-03 00:12:48 -07:00
diced 0e0738f2fe fix: add scripts to dockerfile 2025-09-03 00:07:38 -07:00
diced 97b8483eeb fix: remove skip build 2025-09-03 00:04:27 -07:00
diced 3f0306e436 fix: remove extra steps 2025-09-03 00:03:08 -07:00
diced 87650d0fec feat: new scripts system 2025-09-03 00:00:04 -07:00
diced 0a59298fa0 chore: update to zod@4 2025-09-02 23:38:23 -07:00
diced 8e778d4178 fix: user not being included on text files (#871) 2025-09-02 16:18:22 -07:00
diced a92f072d62 fix: password being reset when editing urls (#872) 2025-09-02 15:53:56 -07:00
diced 003dba9ce4 fix: show more information on client errors 2025-09-02 15:53:22 -07:00
diced fd8d4fbe5e fix: don't allow deselecting in selects 2025-08-28 11:58:08 -07:00
diced ac37f13452 feat: thumbnails output format (jpg, png, webp) 2025-08-27 21:18:46 -07:00
diced ef13ef755c feat: default image compression type 2025-08-27 17:26:19 -07:00
diced fdb0312dbe feat: compression formats 2025-08-27 16:42:36 -07:00
diced 95042e1383 fix: silently error out when no git sha #864 2025-08-25 15:03:43 -07:00
diced f75020b115 fix: metrics admin only (#863) 2025-08-25 14:36:49 -07:00
diced 24ad601e2a fix: date normalization in ssr 2025-08-23 12:18:50 -07:00
diced 771811b4b7 chore: update packages 2025-08-21 15:03:26 -07:00
diced 459f99d507 feat: pdf rendering in dashboard
uses builtin browser renderer, basically every modern browser will work
2025-08-20 20:51:41 -07:00
diced 6758fe1037 feat: asciinema in dashboard rendering 2025-08-20 20:40:24 -07:00
diced b48e9ba1e4 fix: reject partials on normal upload 2025-08-20 15:57:25 -07:00
diced a9c7d694eb fix: z-index for dropzone 2025-08-19 15:25:31 -07:00
diced 18c428532f fix: use public endpoint for domains 2025-08-19 15:09:29 -07:00
diced 6acbf00b9e fix: linting 2025-08-18 12:39:25 -07:00
diced 471a060df2 fix: faulty domains code + errorboundary 2025-08-18 12:38:44 -07:00
diced 9cfb01cd88 fix: bug template error 2025-08-18 11:56:01 -07:00
diced 6442f5f3dc fix: new bug template 2025-08-18 11:53:19 -07:00
diced c43afc1145 feat: extra css property for themes
allows adding extra css to custom themes, useful for loading fonts, etc.
2025-08-16 14:46:28 -07:00
diced 8a5972c517 fix: ishare icon 2025-08-14 16:56:24 -07:00
diced f6eefc01e2 fix: build stage order 2025-08-14 12:34:21 -07:00
dicedtomato ae7b4dacf1 feat: remove next.js in favor of client-side only (#857)
* feat: start removing next.js

* feat: working ssr + dev + prod env

* feat: all functionality added + client/ -> src/client/

* fix: build process

* fix: caching on pnpm action

* fix: ignores + cache action

* fix: docker + exdev error

* fix: generate prisma before types

* fix: remove node@20 from actions

* feat: dynamic import optimizations + titled pages

* fix: removed unused vars

* feat: small ui fixes and improvements

* feat: small ui improvements

* fix: linting error

* fix: regex when adding domains
2025-08-14 12:13:54 -07:00
diced 71dbbb584a feat(v4.2.3): version 2025-08-09 22:46:30 -07:00
Snipcola f03bd74865 fix: wrong env vars (#858)
* capitalize `random words separator` environment variable

* change `RATELIMIT_WINDOW` environment variable type to number
2025-08-09 22:42:01 -07:00
diced f059dcca35 fix: once and for all fix #854 2025-08-08 22:47:51 -07:00
diced 531ba13daf fix: no longer use rename since it's weird 2025-08-08 15:02:38 -07:00
diced cd8b892a90 feat(v4.2.2): version 2025-08-07 19:48:52 -07:00
diced 3575981984 fix: exdev error workaround #856 2025-08-07 19:31:56 -07:00
dicedtomato 81c880b1ca Merge commit from fork 2025-08-07 19:29:28 -07:00
diced 9b8e57bda0 fix: do not add new sessions on session save (#855) 2025-08-04 11:44:06 -07:00
diced 4a8f90a901 fix: #855 session override bug 2025-08-03 16:24:00 -07:00
diced 6acdc72776 fix: multiple db connections on offloaded threads 2025-08-02 16:53:53 -07:00
diced f78c873aae fix: revert zod 2025-08-02 16:52:14 -07:00
diced 0f82bf8d90 fix: formatting errors 2025-08-02 16:52:03 -07:00
diced 82a7f1d0bf feat(prisma): use non-rust engines 2025-08-02 16:36:08 -07:00
diced 2fd1007e4b chore: lint + upgrade packages 2025-08-02 15:40:09 -07:00
diced c360235fa8 fix: better thumbnail logic 2025-08-02 15:29:27 -07:00
diced a4404f1ae8 fix: refactor routes to be separated 2025-08-02 11:25:16 -07:00
diced 56d1492377 feat: ability to rename files 2025-08-01 16:43:20 -07:00
diced fa9bf185d5 fix: improve logic in uploading + partial 2025-08-01 12:31:07 -07:00
diced eca6a0c5fd feat(unstable): implement new uploading logic 2025-07-31 23:23:31 -07:00
diced f58ed2f368 fix: add minio to flake 2025-07-31 23:22:06 -07:00
diced 64c39dab76 fix: update nix flake to use devenv 2025-07-31 20:22:10 -07:00
diced ac08f4f797 feat(v4.2.1): version 2025-07-28 12:21:26 -07:00
diced 91a2c05d3b feat: nix dev shell 2025-07-27 12:34:25 -07:00
diced 3ccc108d43 fix: search by id color 2025-07-19 14:32:34 -07:00
diced aaaf0cf5aa fix: prolly fix #843 2025-07-19 14:27:40 -07:00
diced db7cf70bca fix: favorite transactional 2025-07-11 11:47:58 -07:00
diced 8b59e1dc53 fix: properly handle custom components 2025-07-08 19:34:59 -07:00
diced da066db07e fix: discord oauth #833 2025-07-04 14:19:46 -07:00
diced b566d13c8d fix: random visual bugs + enhancements 2025-07-02 20:41:37 -07:00
diced 6a76c5243f fix: typo separator 2025-07-02 14:12:35 -07:00
curet 38a90787d0 feat: predefined domains (#822)
* feat(domains): add domains to server settings

* fix(domains): fix linting errors

* fix(domains): remove unused imports

* fix(urls): fix typo

* feat(domains): remove expiration date from domains

* feat(domains): changed domains from JSONB to TEXT[]

* fix(domains): linter errors

---------

Co-authored-by: dicedtomato <35403473+diced@users.noreply.github.com>
2025-07-02 10:52:33 -07:00
diced 4652ada85e feat(v4.2.0): version 2025-07-01 17:43:12 -07:00
diced 5f96c762e0 fix: lint errors 2025-07-01 17:30:49 -07:00
diced 651f32e7ba fix: remove split user/pass error 2025-07-01 17:27:32 -07:00
diced dcbd9e40f0 fix: use absolute path for mac flameshot 2025-07-01 17:22:19 -07:00
diced 3486e9880e feat: midnight pink theme 2025-07-01 17:15:41 -07:00
diced b058c15f26 fix: up cookie age 2 weeks 2025-07-01 16:58:35 -07:00
diced 96f60edaee fix: try to fix insane db connections #778 2025-07-01 16:55:57 -07:00
diced d7f3e1503f fix: broken link partial file #816 2025-07-01 15:53:20 -07:00
diced dfc8fca3e0 fix: default expiration #821 2025-07-01 15:33:29 -07:00
lajczi 28f7d3f618 chore: update ESLint config (#826)
* chore: update ESLint config

* chore: update file permissions

---------

Co-authored-by: dicedtomato <35403473+diced@users.noreply.github.com>
2025-07-01 11:38:47 -07:00
curet 5c0830c6da fix: long code blocks (#823) (#810) 2025-07-01 10:58:25 -07:00
diced ef33fcbe1d fix: lint error 2025-06-11 20:23:25 -07:00
diced 4b1ca07510 feat: better cache for versions 2025-06-11 20:21:52 -07:00
diced 438b9b5a67 feat: show alert when there are overridden settings 2025-06-08 12:02:51 -07:00
diced ed1273efba feat: convert db settings to env vars cli 2025-06-08 11:52:32 -07:00
diced e8518f92c7 fix: remove 2025-06-07 11:36:51 -07:00
diced fbf9e10e56 feat: allow/denylist discord oauth 2025-06-07 11:36:23 -07:00
diced a1ee1178ae feat: allow env vars that override database set settings 2025-06-07 11:17:43 -07:00
diced e5eaaca5a0 feat: discord oauth whitelist 2025-06-06 20:33:41 -07:00
diced 6e9dea989e fix: use cmd icon on mac 2025-06-06 15:15:11 -07:00
diced 5bc9b6ef0a feat: add download button to file table view 2025-06-06 15:10:13 -07:00
diced 6362d06253 feat: new gps remover 2025-06-06 15:06:21 -07:00
diced 81866b4b50 feat(v4.1.2): version hotfix 2025-06-06 10:40:57 -07:00
diced 4b3878d553 feat: switch metadata remover 2025-06-06 10:40:38 -07:00
diced d0a613ab8e feat(v4.1.1): version 2025-06-05 22:52:44 -07:00
diced 1bff0564e7 feat: ratelimits for a bunch of routes (small) 2025-06-05 22:52:23 -07:00
diced df449b1bcb fix: passkeys route, deleting works now 2025-06-05 15:08:11 -07:00
diced bd057944ce chore: update dependencies 2025-06-05 14:53:03 -07:00
diced 856fa00d1d fix: linting errors 2025-06-04 23:54:58 -07:00
diced 1703cee75a fix: #817 2025-06-04 23:53:04 -07:00
diced 0a970da241 fix: implement workaround for video-audio contains thumbnail 2025-06-01 12:14:42 -07:00
diced 04b0a18b85 fix: remove ignore tls verify 2025-06-01 11:33:40 -07:00
diced e7de1c9762 fix: show error when s3 fails 2025-06-01 11:03:12 -07:00
Josh 2df9098586 feat: add service_healthy + healthcheck to docker-compose (#811)
* Update docker-compose.yml

Since you use healthcheck anyway, you get to verify that postgres is healthy

* Update README.md

Update the README with 'depends_on: postgresql:  condition: service_healthy'

* Add zipline healthcheck to docker-compose.yml

* Add docker-compose healthcheck for zipline to README.md

* Update docker-compose.yml

---------

Co-authored-by: dicedtomato <35403473+diced@users.noreply.github.com>
2025-05-31 15:44:14 -07:00
diced e8380cc261 fix: allow unauthorized certs s3 2025-05-26 12:26:09 -07:00
diced 71a1ed9072 fix: stop scroll when zoomed in 2025-05-26 12:25:25 -07:00
diced 6b0bbad8d4 feat(v4.1.0): version 2025-05-20 20:58:51 -07:00
diced 8f12621315 feat: new view file showing options 2025-05-20 20:55:52 -07:00
diced e5ee076e08 fix: better image width/height handling 2025-05-20 19:50:17 -07:00
diced 8382a1b55d fix: remove some paths from safe config 2025-05-18 22:03:44 -07:00
diced a35d8b87ee feat: more version checking options 2025-05-15 21:09:44 -07:00
diced f70eea97b0 fix: better DEBUG var handling 2025-05-15 15:09:19 -07:00
diced 7ab5c4e180 fix: add more debug logging when failing oauth 2025-05-13 15:58:20 -07:00
lajczi 486165625d ci: node.js 23 -> 24 (#809) 2025-05-13 14:47:14 -07:00
diced 08eb2df26c fix: typings for version api 2025-05-08 21:29:51 -07:00
diced 4a5d01c663 feat: version checking 2025-05-08 21:20:47 -07:00
diced 485f106a65 fix: remove avatar fetching every 30s 2025-05-08 17:15:33 -07:00
diced 3d3f519403 feat: s3 subdirectory 2025-05-08 15:13:31 -07:00
diced 617f42d3bf fix: remove prisma schema load message 2025-05-08 10:59:18 -07:00
diced 25a2a54d8a fix: unused imports in s3 2025-05-07 20:04:14 -07:00
diced 35c37c235f fix: change access test logic 2025-05-07 19:56:02 -07:00
diced 594dfa6ef9 fix: import issue 2025-05-06 01:19:08 -07:00
diced 5ab36a08b2 feat: expose git sha in versioning api 2025-05-06 01:14:13 -07:00
diced 90aef3dce1 feat: update mantine, prisma, etc. 2025-05-06 00:17:53 -07:00
diced 8b9303ed80 fix: pnpm 10.10 stuff 2025-05-01 17:49:55 -07:00
diced ee9639ac65 fix: kys 2025-04-29 12:37:34 -07:00
diced 055bee6286 fix: finally fix ts dumb ahh error 2025-04-29 12:35:35 -07:00
diced c3bc598016 fix: remove unused import 2025-04-29 12:33:39 -07:00
diced c0261285af fix: don't overwrite sessions webauthn (#792) 2025-04-29 12:24:51 -07:00
diced 0538b792ac feat: better partial upload checking 2025-04-29 12:14:43 -07:00
rlko 567a855ba1 fix: shell script uploader/shorten (#786)
* fix filenames with spaces + fix mime type uploads

* Add proper MIME type for uploads (especially videos)
  in shell script

* Few fixes for shell script uploader

* encapsulating all headers
* tr not needed (anymore?)
* removing `echo` as it is not returning anything
  because it is already printing on stdout
* parsing correct key for url uploader

* `echo` string seems like not needed anymore

also passed prettier

---------

Co-authored-by: dicedtomato <35403473+diced@users.noreply.github.com>
2025-04-29 11:49:18 -07:00
bigbenster702 2e59f5bd7f fix: discord auto continue on oauth screen (#795) 2025-04-29 11:29:00 -07:00
diced ef0580655d fix: showing upload button on disabled upload folder (#776) 2025-04-16 21:47:17 -07:00
diced 8ece705eb5 fix: add mimetype to s3 uploads (#785) 2025-04-16 21:44:27 -07:00
diced 485fa62ed9 fix: oauth main api route 2025-04-09 23:04:10 -07:00
diced b4819cd038 fix: import-dir script will work now 2025-04-08 19:47:42 -07:00
diced cb2f2daf60 fix: formatting errors 2025-04-05 00:03:43 -07:00
diced c2848f19c1 fix: malformed s3 multipart uploads (#771) 2025-04-04 19:47:01 -07:00
diced 55684528b8 feat: expose theme on view routes 2025-04-03 12:39:05 -07:00
diced 9611e6d5a5 feat: overhaul qs system 2025-04-03 12:32:27 -07:00
diced e8207addba feat(v4.0.2): version 2025-03-31 21:32:42 -07:00
diced d6e52e6dce fix: better s3 error handling (#766) 2025-03-31 21:24:27 -07:00
diced d3250fdc45 fix: remove filtering empty lines in syntax highlighter 2025-03-31 21:20:21 -07:00
diced c44572920b fix: syntax highlighting (#697) 2025-03-31 21:17:42 -07:00
diced 20a7f134ad fix: redirect /view on text files 2025-03-30 21:36:29 -07:00
diced 05d3e99cbb fix: up the body limit for importing v3 2025-03-28 16:29:38 -07:00
diced 369a527446 fix: make .stats optional for imports 2025-03-28 15:46:23 -07:00
diced 92b7024111 fix: maybe fix s3 max sockets issue (#752) + update next.js 2025-03-28 15:44:44 -07:00
curet e1256db661 feat: files per page selector on gallery view (#757)
* feat(files): improved pagination with files per page selector

* style(files): formated imports

* style(files): formated imports

* reverted commit

* feat(files): improved pagination with files per page selector

* Update src/components/pages/files/views/Files.tsx

---------

Co-authored-by: dicedtomato <35403473+diced@users.noreply.github.com>
2025-03-28 15:24:26 -07:00
dicedtomato b767a0082e Merge commit from fork
* fix: math.random not random

* fix: ignore require() warnings
2025-03-19 14:42:13 -07:00
curet c6e536a803 feat: midnight orange/blue themes, fix #754 #751 (#753)
* feat(theme): add midnight orange theme

* feat(files): Added more files to one page & filter by views

* feat(ThemeProvider): Added bigger radius for moderner look
fix(auth): When title to long it fits now better
fix(api): Fixed user favorties not showing right

* fix(auth)

* feat(theme): add midnight blue theme
change(file): updated it 15 files per page

* changed(theme): midnight_orange: add missing colors

* little improvement on the theme files
2025-03-18 14:08:54 -07:00
diced ba6d5eb654 feat(v4.0.1): version 2025-03-12 15:29:49 -07:00
diced 248ac8a63a fix: change security policy for 3.x 2025-03-12 14:50:38 -07:00
diced 41161fb13a fix: tags combobox zindex (#747) 2025-03-12 12:38:20 -07:00
diced c69578cc47 chore: downgrade @aws-sdk/client-s3 to not use checksum headers 2025-03-12 11:51:50 -07:00
diced c9ed28e042 fix: incorrect hex parsing (#736) 2025-03-04 12:08:04 -08:00
diced 01e45a19e7 fix: add more frontend to anon uploads 2025-03-04 11:42:28 -08:00
diced 7d486986db fix: url passwords only on logged in (#739) 2025-03-04 10:51:17 -08:00
diced 20b781709f feat: anonymous folder uploads 2025-03-03 22:26:38 -08:00
diced ba144ab58c fix: remove unused icon 2025-03-03 21:16:25 -08:00
diced ffb9004805 fix: ranged request for non view-route (#724) 2025-03-03 21:01:36 -08:00
diced ab8c6a708a fix: nullable oauthId (#734) 2025-03-03 20:46:23 -08:00
Maddie a3aa1302e1 fix: "continue with provider" instead of sign in (#737)
Update button text to "Continue with Discord" to reflect both sign-in and sign-up functionality.

Co-authored-by: dicedtomato <35403473+diced@users.noreply.github.com>
2025-03-03 15:56:23 -08:00
loefey (nolan) d5049570e6 feat: cross-env & redesign register page
* Updated package.json, pnpm-lock.yaml and register.tsx

Changed the register page to look like the login page, added cross-env package and cross-env to the dev and build scripts in package.json

* Format register.tsx

Jay forced me too :(

* Update package.json

* Update package.json

* Update package.json

---------

Co-authored-by: dicedtomato <35403473+diced@users.noreply.github.com>
2025-03-03 15:53:08 -08:00
nobody 3ab4fc960e feat: random domain selection with x-zipline-domain (#713)
* Update index.ts 

randomize returnDomain if "x-zipline-domain" is a comma separated list

- Updated returnDomain handling to support both single and comma-separated domain strings.
- Implemented random selection from the list of domains.

* Update parseHeaders.ts

randomize returnDomain if "x-zipline-domain" is a comma separated list

- Updated returnDomain handling to support both single and comma-separated domain strings.
- Implemented random selection from the list of domains.

* Update parseHeaders.ts

* Update index.ts

* fixed formatting for prettier

* fixed formatting for prettier

* Update index.ts

* Update parseHeaders.ts

* Update index.ts

---------

Co-authored-by: dicedtomato <35403473+diced@users.noreply.github.com>
2025-02-22 18:08:01 -08:00
diced a90a03a250 feta: fix api errors + gps metadata 2025-02-22 15:12:15 -08:00
diced af68a2a06c feat: move oauth api to fastify + update deps 2025-02-21 23:33:48 -08:00
diced 1d92599788 fix: ishare import issues 2025-02-21 15:18:57 -08:00
diced eae5d755f6 feat: add ishare support 2025-02-21 12:28:43 -08:00
diced ef53a11bdf feat: more debug logging for oauth 2025-02-20 20:32:49 -08:00
diced d81c30a354 fix: double server settings error 2025-02-20 12:26:24 -08:00
diced 0e85c1b5b6 fix: make it clearer that view routes need to be enabled for embeds 2025-02-20 12:26:10 -08:00
diced d407b9397c fix: title overflow! 2025-02-20 12:23:33 -08:00
diced 683b77503e fix: remove core_secret from dev docker-compose 2025-02-20 12:14:52 -08:00
diced 76fae35ed6 fix: add unless-stopped + switch tag to latest 2025-02-20 12:13:13 -08:00
diced 1801287deb fix: reload nextjs api route settings on saves 2025-02-20 11:48:57 -08:00
diced 60e9b52100 fix: disable passkey button when it's disabled 2025-02-20 11:14:05 -08:00
diced a97decfb0c fix: #709 2025-02-18 12:46:53 -08:00
diced 7a93070958 fix: add a warning to restart oauth if any issues 2025-02-17 12:03:44 -08:00
diced 316d961fb6 fix: do not overwrite sessions on oauth logins 2025-02-17 12:00:55 -08:00
diced a4b55d102a fix: oidc has a default oauth state 2025-02-17 11:56:14 -08:00
diced ec8e426964 fix: docker push action again 2025-02-17 11:35:29 -08:00
diced 9cacb84e3a fix: docker push action 2025-02-17 11:23:11 -08:00
diced 7aee51e81c fix: push to dockerhub 2025-02-17 11:14:07 -08:00
diced 4effcc2538 fix: #698 2025-02-17 11:05:25 -08:00
Jay 56801d8770 feat: add github issue template (#696)
* feat: add github issue template

* Update .github/ISSUE_TEMPLATE/bug.yml

* Update .github/ISSUE_TEMPLATE/bug.yml

---------

Co-authored-by: dicedtomato <35403473+diced@users.noreply.github.com>
2025-02-16 23:19:44 -08:00
diced e1905c7fb3 feat: add a redirect for old /r/ urls 2025-02-16 22:11:40 -08:00
diced ce7ebcbe27 fix: use custom redirect uri if provided 2025-02-16 21:49:43 -08:00
diced aebb3e53f4 fix: ctl paths 2025-02-16 21:29:34 -08:00
diced 7e1cec2e44 fix: allow null password on imports 2025-02-16 20:27:11 -08:00
diced b842d594f7 fix: action again 2025-02-16 17:44:05 -08:00
diced 7961c745f3 fix: action 2025-02-16 17:36:55 -08:00
diced 477aca289a feat: v4 changes 2025-02-16 17:25:11 -08:00
diced a6807a681c feat: new readme 2025-02-15 16:44:40 -08:00
diced 7bd889ebd9 fix: small stuff 2025-02-13 21:52:35 -08:00
diced cbd9191488 feat: gfycat style file names 2025-02-13 21:07:32 -08:00
diced 9607bfcb66 fix: defaults for core_hostname 2025-02-13 15:13:20 -08:00
diced 8d6f9e344e feat: bunch of icons for files 2025-02-13 15:08:53 -08:00
diced 171790613d feat: common double-extensions like .tar.gz supported 2025-02-12 23:24:44 -08:00
diced 86b4e5fdcb fix: remove console.log 2025-02-12 22:35:09 -08:00
diced 6c19a392fc feat: fix theming issues 2025-02-12 22:34:10 -08:00
diced 0e825ceca0 fix: unused icon 2025-02-10 21:28:45 -08:00
diced bb5c774c74 feat: image zoom in on view-route 2025-02-10 21:20:04 -08:00
diced e24647afcd fix: default upload dir 2025-02-10 20:58:50 -08:00
diced 46514cba31 fix: core_secret validation 2025-02-07 12:48:45 -08:00
diced fff1d82d8e fix: make invite url copyable through a.href 2025-02-03 17:21:57 -08:00
diced 711e583518 fix: add edit name to card view 2025-02-03 17:19:11 -08:00
diced c9ecc46e8c feat: edit folder names 2025-02-03 17:16:24 -08:00
diced 6edeb78373 fix: add nextjs action caching 2025-02-03 16:12:02 -08:00
diced 333ea0b949 feat: amd64+arm64 builds to ensure building ffs 2025-02-03 16:04:34 -08:00
diced f67ab2a9d8 chore: update pnpm version 2025-02-03 15:50:56 -08:00
diced fefb9dfd40 fix: possibly fix corepack being dumb 2025-02-03 15:48:21 -08:00
diced ee096249b0 chore: update docker file node/alpine version 2025-02-03 15:39:41 -08:00
diced 8dc8e7cc75 fix: login interval + add text for register 2025-02-02 23:31:25 -08:00
diced eaebb80ee7 fix: clarify settings + better validation 2025-02-02 23:12:49 -08:00
diced e58c069ec3 fix: unnecessary escape 2025-02-02 17:47:11 -08:00
diced afbda50a21 fix: ssl 2025-02-02 17:11:39 -08:00
diced 321366ca86 feat: remove searchThreshold as it's no longer used 2025-02-01 17:56:05 -08:00
diced 827e76bd86 fix: show user passkey errors 2025-02-01 17:45:25 -08:00
diced 581a46f7d4 fix: naming on 2fa modal 2025-02-01 17:40:58 -08:00
diced 139c3e14ad feat: percent change indicator for metrics 2025-02-01 17:34:17 -08:00
diced af48f91aa1 fix: bug fix + overhaul metrics 2025-02-01 17:03:43 -08:00
diced f2621572b0 feat: change default theme from gray to blue 2025-02-01 16:12:43 -08:00
diced eb5117d7ee fix: ID searching uses contains + insensitive 2025-02-01 16:04:42 -08:00
diced 51e6cf92a4 fix: replace notFound with next 404 2025-02-01 16:02:54 -08:00
diced 401861116d fix: only show server-settings menu link for super-admins 2025-02-01 15:57:01 -08:00
diced 586c17b320 fix: actions arm notation 2025-01-31 15:24:52 -08:00
diced 3a698652ab fix: actions matrix fix 2025-01-31 15:22:50 -08:00
diced e07d02f39d feat: try matrix docker action 2025-01-31 15:21:12 -08:00
diced 6b8b29ed29 feat: enable/disable urls on the fly 2025-01-31 13:12:18 -08:00
diced 76d0c0786a fix: improve maxViews/deletesAt for files and urls 2025-01-31 12:39:41 -08:00
diced 9893e38442 fix: improve view routes 2025-01-31 12:20:28 -08:00
diced 00ee02dc50 fix: only show settings if super-admin 2025-01-30 20:04:57 -08:00
diced 5b5713b7c1 feat: redesigned login 2025-01-30 20:02:16 -08:00
diced 644c34a5ad feat: 404/500 custom pages 2025-01-28 00:36:46 -08:00
diced 8957c8af4c feat: search files by id 2025-01-26 15:50:36 -08:00
diced 5cf6a8b53d fix: tag checks 2025-01-26 14:28:30 -08:00
diced 1be72ba9d9 fix: update a user's role 2025-01-26 00:45:48 -08:00
diced 506bab475c fix: weird file password issues 2025-01-26 00:42:14 -08:00
diced b793370ae8 feat(actions): try ubuntu-24.04-arm runner 2025-01-20 20:44:46 -08:00
diced 2130d113d4 fix: oidc: don't wish for a refresh token 2025-01-19 17:55:24 -08:00
diced 0caa188aa3 fix: built version now reloads config changes 2025-01-17 17:42:12 -08:00
diced d85b0e32b8 fix: move route files 2025-01-17 17:10:49 -08:00
diced bed8a5df03 feat: implement /robots.txt 2025-01-17 17:05:05 -08:00
Stefano Del Prete 33522fcdc6 fix: conditional modifiers fix (#667)
* should be signed

* remove boolean modifiers that cna be replaced by istrue/isfalse

* added exists check on string, removed isfalse check on boolean since it the same as istrue but inverting the true/false strings

* add ::exists conditional modifier to date since {file.deletesAt} can be null

* Fix parser regex

before this fix, here `{file.createdAt::locale::it-IT,Europe/Rome}{file.id}` it would matcg `it-IT,Europe/Rome}{file.id` as locale timezone

here `{file.name::exists["yes"||"no"]}{file.originalName::exists["yes"||"no"]}` it would match `yes"||"no"]}{file.originalName::exists["yes"||"no` as `mod_check_true` group

* Forgot `\` before first `{` in the regex

* add conditional mods to originalName too, fix regex not matching when true/false string had "\n", fix "::exists" matching on string when they were "null"

---------

Co-authored-by: dicedtomato <35403473+diced@users.noreply.github.com>
2025-01-17 16:59:40 -08:00
diced 85699e658c fix: clear folder value after upload 2025-01-16 20:25:00 -08:00
Stefano Del Prete 7ea8207c30 feat: exists conditional modifier to date, fix parser regex (#666)
* should be signed

* remove boolean modifiers that cna be replaced by istrue/isfalse

* added exists check on string, removed isfalse check on boolean since it the same as istrue but inverting the true/false strings

* add ::exists conditional modifier to date since {file.deletesAt} can be null

* Fix parser regex

before this fix, here `{file.createdAt::locale::it-IT,Europe/Rome}{file.id}` it would matcg `it-IT,Europe/Rome}{file.id` as locale timezone

here `{file.name::exists["yes"||"no"]}{file.originalName::exists["yes"||"no"]}` it would match `yes"||"no"]}{file.originalName::exists["yes"||"no` as `mod_check_true` group

* Forgot `\` before first `{` in the regex
2025-01-16 11:49:27 -08:00
Dylan f68d670600 fix: sharex url shortening config (#664)
V4 API requires destination, not url anymore. This should fix the generated config for url shortening.
2025-01-13 17:00:48 -08:00
diced 084feadc01 feat: support for non-english characters, japanese, korean, chinese, etc. 2025-01-13 12:56:25 -08:00
diced 46e16f6dc8 fix: title responsiveness 2025-01-13 12:27:28 -08:00
diced 768b84a1a7 fix: only log fastify errors on 500 2025-01-13 12:12:58 -08:00
diced 9e25062835 fix: bigint conversion (#663) 2025-01-13 12:06:40 -08:00
diced eea0c093dd fix: pnpm@9.15.4 fixes 429 2025-01-13 11:50:47 -08:00
Stefano Del Prete 0464d0da18 feat: conditional modifiers for variables (#662)
* should be signed

* remove boolean modifiers that cna be replaced by istrue/isfalse

* added exists check on string, removed isfalse check on boolean since it the same as istrue but inverting the true/false strings
2025-01-13 11:38:29 -08:00
diced 3848064f90 fix: discord webhook config 2025-01-11 14:59:03 -08:00
diced 329e0c969a fix: reload not resolving as a next route 2025-01-11 13:22:50 -08:00
diced 5f026bd2f3 feat: loginBackgroundBlur option 2025-01-11 13:22:42 -08:00
diced 84d5066b1b fix: favicon + weird title errors 2025-01-10 12:24:05 -08:00
diced 863d68695f feat: support website title 2025-01-10 12:07:53 -08:00
diced 4a2988914a fix: /dashboard not building/resolving 2025-01-10 11:56:20 -08:00
diced 1513cb295b fix: images not showing up on firefox 2025-01-10 11:38:34 -08:00
diced f5d4b1845d fix: issue with pnpm action & update ubuntu-latest 2025-01-09 13:19:38 -08:00
diced e7a437c2f8 feat: update action versions 2025-01-09 13:15:11 -08:00
diced fa4d21ecac feat: change versions in actions 2025-01-09 12:42:20 -08:00
Arlind 9d7f2915df feat: add tzdata to dockerfile for timezones 2025-01-09 12:03:48 -08:00
Creation's c0311283a5 feat: add DATASOURCE_S3_FORCE_PATH_STYLE (#658)
Co-authored-by: dicedtomato <35403473+diced@users.noreply.github.com>
2025-01-08 22:28:18 -08:00
diced d6ef09f5b3 fix: uncomment meta tags (#652) 2025-01-08 22:22:07 -08:00
diced ff4a5c9d74 fix: diff icon for server settings 2025-01-08 22:20:05 -08:00
diced 3d364c7d57 fix: remove debug log 2025-01-08 22:16:29 -08:00
diced 9a117d7032 feat: string bytes/ms settings so they wont overflow 2025-01-08 22:15:27 -08:00
diced 49fb0434bd fix: linux scripts 2025-01-08 01:05:02 -08:00
diced b70293182d feat: add links to oauth setup providers directly 2025-01-06 17:27:56 -08:00
diced 68f46ef9f7 fix: original name for partial upload (#655) 2025-01-06 17:08:27 -08:00
diced 2ed06a6bf7 fix: hover on text files (#650) 2025-01-04 15:31:29 -08:00
diced f16d507bfc fix: long urls get wrapped 2025-01-03 16:01:52 -08:00
diced fa39ac77d4 fix: non-view route password protected file 2025-01-03 15:55:54 -08:00
diced a96b99c97d fix: defaultExpiration as a string now 2025-01-03 15:46:19 -08:00
diced 4a3e83a387 fix: add ffmpeg to dockerfile 2025-01-03 15:30:34 -08:00
diced 252df5b9fb fix: scripts 2025-01-03 15:15:10 -08:00
diced 651efbc047 feat: prisma migrations 2024-12-28 15:51:59 -08:00
diced 6f044c6f35 feat: merge data from an export3 2024-12-28 14:37:39 -08:00
diced c8bf8aa18b fix: explicitly state production mode for react-jsx in docker 2024-12-23 11:13:31 -08:00
diced a55a43d99f fix: allow s3 endpoints 2024-12-20 15:40:58 -08:00
diced 12fcff1a14 fix: a bunch of random stuff 2024-12-20 00:07:33 -08:00
diced dcb4a4e9e7 fix: types for export3 2024-12-16 15:54:19 -08:00
diced 4d472a167c fix: more export validation 2024-12-16 15:47:21 -08:00
diced 57e9790112 fix: cookies should work on all browser 2024-12-16 15:41:25 -08:00
diced 7f7764f1df fix: remove themes dir from dockerfile 2024-12-16 14:46:20 -08:00
diced d1cef2c56d feat: skeletons for most ui components 2024-12-16 14:40:41 -08:00
diced 2af85b4fcf fix: theming issues & all included themes are builtin 2024-12-13 22:12:38 -08:00
diced 4cdfee211b fix: error message notifications use right prop now 2024-12-13 21:54:18 -08:00
diced dcb6b287fa feat: --skip-next flag to skip loading nextjs 2024-12-13 15:44:16 -08:00
diced cf3c92e045 feat: importing v3 exports 2024-12-13 15:43:57 -08:00
diced 8d123bc517 fix: close modals server actions 2024-12-13 15:42:12 -08:00
diced f6ad990a4f fix: ctl location in scripts 2024-12-13 15:39:37 -08:00
diced 8b5d7b5e8e fix: send user instead of current user 2024-12-13 14:43:34 -08:00
diced 657b3ed2b2 fix: better error handling for github oauth 2024-12-05 14:18:23 -08:00
diced 05a4e08fba fix: link state even though not linking (#637) 2024-12-05 14:12:18 -08:00
diced 47cbca7826 fix: use same blur properties 2024-12-03 23:00:16 -08:00
diced 7b0a543cd6 feat: take query for recent 2024-12-03 22:49:33 -08:00
diced 4c3f2bff72 feat: more logging 2024-12-03 22:38:31 -08:00
diced 1bbba6bcda fix: add a todo to the setup 2024-12-03 00:29:41 -08:00
diced cac8dd6c2f fix: validate secret >= 32 2024-12-03 00:13:50 -08:00
diced a13cb82201 fix: limit text viewing to 1mb 2024-12-03 00:11:22 -08:00
diced d02e3a92b4 fix: add no exports paper 2024-12-03 00:07:05 -08:00
diced 5f9762fd3e fix: token & xshare app compatibility 2024-11-29 16:08:34 -08:00
diced da875e451c fix: oauth state is encrypted 2024-11-25 22:36:55 -08:00
diced 523219273a fix: various small fixes 2024-11-14 15:34:32 -08:00
diced e6db5e1bdc fix: scrollbar showing up for no reason 2024-11-14 14:53:47 -08:00
diced 61d48a83f0 fix: metrics not loading 2024-11-14 14:46:33 -08:00
Seaswimmer 49269f77fb feat: add Catppuccin themes (#562)
* feat: add catppuccin mocha theme

* feat(catppuccin mocha): use a slightly lighter shade of black for the background

* fix(catppuccin mocha): fixed the mainBackgroundColor being too dark

* feat: add Catppuccin Frappé

* feat: add Catppuccin Macchiato

* feat: add Catppuccin Latte

* fix(catppuccin latte): use dark mode so colors are applied properly

* fix(themes/catppuccin latte): made it a light theme and changed colors

thanks @diced for information regarding this in my pr
2024-11-14 14:01:49 -08:00
diced 129ba0cdea feat: edit urls 2024-11-12 22:18:11 -08:00
diced 0deeb0f279 fix: build errors 2024-10-23 15:35:12 -07:00
diced 727c8bd808 fix: clickable links 2024-10-23 15:22:04 -07:00
diced f9b2064019 feat: show default options in upload options 2024-10-16 12:58:30 -07:00
diced 3d6aaa43d9 feat: custom redirect uris for oauth 2024-10-16 11:32:34 -07:00
diced 406a4f3fdb feat: visual changes from mantine@v7.12 2024-10-16 11:08:43 -07:00
diced 76b558627c chore: update deps 2024-09-22 21:19:09 -07:00
diced a62e03b439 feat: pwa 2024-09-22 15:10:36 -07:00
diced fbb1f54b27 feat: new docker compose vars 2024-09-22 15:08:30 -07:00
diced 2a99fae546 fix: themes weird 2024-09-22 15:08:01 -07:00
diced 67c0813657 feat: request regeneration of video thumbnails 2024-09-15 15:52:45 -07:00
diced 7eb45758c8 fix: dot-prop cjs #608 2024-09-13 21:17:22 -07:00
diced 8db6d5f0c7 fix: a bunch of small fixes 2024-09-13 17:22:57 -07:00
diced 96a6fe577e fix: random type errors 2024-09-12 16:33:40 -07:00
diced bd774b8ffb fix: exports path 2024-09-12 16:17:04 -07:00
diced 641ec235d9 feat: database based server settings 2024-09-12 15:54:38 -07:00
diced 382e4d69d6 fix: remove unnecessary type casts 2024-09-05 19:52:57 -07:00
diced e9348c127b fix: remove session on logout 2024-09-05 19:49:22 -07:00
diced f86eba8e55 feat: session management 2024-09-05 18:01:11 -07:00
diced 67a7d44198 fix: sessions & authentik -> oidc & #603 2024-09-04 22:55:15 -07:00
diced 708b130002 fix: add offline_access scope to oidc (#602) 2024-09-03 22:17:36 -07:00
diced f636e042c5 fix: token state 2024-09-01 22:41:52 -07:00
diced 1d5e546851 feat: sessions 2024-09-01 22:35:16 -07:00
diced db28b06791 fix: sharex generator URL (#593) 2024-08-25 21:39:12 -07:00
diced 6cdc38f0a8 fix: metrics storing "null" values (#594) 2024-08-25 21:31:57 -07:00
diced ba3aaf6bb2 fix: invalid sharex header (#592) 2024-08-25 21:17:24 -07:00
diced 90de25b721 fix: empty body parsing (#595) 2024-08-25 21:15:16 -07:00
diced afca5874c0 feat: export files 2024-08-25 11:28:45 -07:00
diced b1c00b8c39 feat: terms of service 2024-07-31 21:19:25 -07:00
diced 049fb631d1 fix: clean up comments regarding s3 2024-07-13 23:48:40 -07:00
diced ea41a50a14 feat: somewhat incomplete s3 support 2024-07-13 23:47:40 -07:00
diced 7a0cfc9391 fix: hide incompleteFiles in get files 2024-07-13 23:47:24 -07:00
diced da729abafb feat: change how metrics is displayed 2024-07-11 15:29:20 -07:00
diced 7ec1d6fac2 feat: change up login background 2024-07-11 15:03:44 -07:00
diced f8295790de feat: native ssl 2024-06-23 13:28:07 -07:00
diced 26746da068 feat: compressed file icons + document file icons 2024-06-17 21:16:20 -07:00
diced 02496fb3a4 fix: scroll areas + add missing scrollareas to metrics tables 2024-06-17 20:51:36 -07:00
diced d887e6cff9 fix: scrolling types table 2024-06-17 20:45:41 -07:00
diced 040103ed18 refactor: scheduler -> tasks 2024-06-16 20:02:04 -07:00
diced ca766bb1d8 feat: http webhooks 2024-06-13 22:27:13 -07:00
diced 080a3a2c19 feat: add to folder from dashboard uploader 2024-06-13 21:52:23 -07:00
diced c8f63e084b fix: add text for no pending files 2024-06-13 21:39:37 -07:00
diced cf86a4f0c3 fix: padding on DashboardFileType 2024-06-13 21:36:59 -07:00
diced 988e769b63 fix: timings on partial uploads 2024-06-13 21:31:40 -07:00
diced 449461b1fc feat: upload progress and speed 2024-06-10 21:44:06 -07:00
diced 13f99945fc feat: change the way images are viewed + new stuff in modal 2024-06-08 00:34:49 -07:00
diced 4fc289a41a feat: appshell background customization 2024-06-07 22:23:53 -07:00
Vetlix bc0b463f3e fix: incorrect password autocompletes (#557)
* fix: correct password autocompletes

* fix: file view inconsistency

---------

Co-authored-by: dicedtomato <35403473+diced@users.noreply.github.com>
2024-06-03 20:50:53 -07:00
diced 3c5b5bcdc9 fix: update pnpm version 2024-06-03 20:46:24 -07:00
diced fe4a8fc1d0 fix: see if this fixes actions lol 2024-06-03 20:45:25 -07:00
diced 75a1d17ff7 fix: thumbnail generation (better now) 2024-06-02 16:03:57 -07:00
diced 6de3260a2b fix: small bugs when uploading 2024-06-02 16:03:41 -07:00
diced 396a02e0e5 fix: a bunch of small fixes & bug squashing 2024-06-01 22:18:55 -07:00
diced eda7b28836 feat: logo next to title (titleLogo) 2024-06-01 17:26:04 -07:00
diced fd85ea4143 fix: table view editfilemodal 2024-06-01 17:19:29 -07:00
diced 32a295e3f0 feat: ratelimit 2024-06-01 17:19:16 -07:00
diced 1b6bdd8634 feat: usage instructions 2024-05-30 22:56:12 -07:00
diced 26814e7cd2 fix: some docker changes 2024-05-30 22:56:05 -07:00
diced 62d4658b6b fix: finally fix uploads 2024-05-30 22:26:04 -07:00
diced 927e5bd00a fix: i dont know (very unstable commit) 2024-05-22 21:18:55 -07:00
diced fc98015950 fix: password being set when empty 2024-04-24 20:48:26 -07:00
diced e997250cf8 feat: update pnpm 2024-04-24 20:48:13 -07:00
diced 9ec6f2bfcb refactor: move other api routes 2024-04-24 20:44:06 -07:00
diced 3e79534e39 refactor: express -> fastify 2024-04-23 22:23:14 -07:00
diced aaef35ca1b fix: remove log 2024-04-16 20:34:29 -07:00
diced 7e137c0991 feat: storage/file quota + urls quota 2024-04-16 20:34:04 -07:00
diced f1e2d50fd5 fix: errors to build 2024-04-15 21:46:15 -07:00
diced eb5467ec61 fix: deletes at date 2024-04-15 21:33:02 -07:00
diced 38df9c3349 fix: remove random logs 2024-04-15 21:31:31 -07:00
diced d90c6afe01 feat: edit file details ui 2024-04-15 21:31:20 -07:00
diced 164b01c36d feat: pending files ui 2024-04-15 20:41:43 -07:00
diced 23463af804 feat: copy all urls button 2024-03-26 23:38:48 -07:00
diced e6a09b542e feat: ctrl+v to paste image 2024-03-26 23:38:33 -07:00
diced c80ff22d92 feat: http content-range/range support 2024-03-26 23:25:48 -07:00
diced b02bcfc035 feat: url passwords 2024-03-06 21:37:07 -08:00
diced 076a04b55e feat: x-zipline-file-extension 2024-03-06 20:39:56 -08:00
diced 90a6a6329a feat: ziplinectl 2024-02-29 21:04:40 -08:00
diced bc3baa0a27 feat: hide worker logs for spam 2024-02-29 19:50:38 -08:00
diced 2d8cfbdaf2 fix: nodejs@21.x latest 2024-02-21 16:32:10 -08:00
diced b298f7d815 fix: stop manifest listing 2024-02-21 16:25:06 -08:00
diced 46a0e41d2d fix: ammend-builds run 2024-02-21 16:07:44 -08:00
diced 9413e9d42a feat: limit metrics to admin only or not 2024-02-12 21:33:48 -08:00
diced 96729814a0 fix: sorting on metrics 2024-02-12 21:20:48 -08:00
diced 38a303f332 fix: partial uploads 2024-02-12 21:15:10 -08:00
diced 7f969b2084 feat: partial uploads (buggy) 2024-02-11 23:24:53 -08:00
diced 7e9d465090 feat: scripts on dashboard 2024-02-11 15:32:32 -08:00
diced 3a3cf519e6 feat: metrics in variables 2024-02-11 12:16:23 -08:00
diced 34e8b7765b fix: css module issue 2024-01-06 21:04:48 -08:00
diced 987310143e feat: discord webhooks & new parser stuff 2023-12-29 15:32:48 -08:00
diced 61c892f8d8 feat: hide user specific data (privacy feature?) 2023-12-07 22:41:37 -08:00
diced a2e235d2ac feat: tags & file meta editing backend 2023-12-04 23:02:05 -08:00
diced 72e36374fb feat: ZIPLINE_DB_LOG for prisma logs 2023-12-03 12:33:02 -08:00
diced 2ed0ceb386 feat: bulk move files into folder 2023-12-03 12:29:00 -08:00
diced 1b79f9f0d4 fix: remove word similarity & use ILIKE operator 2023-12-02 16:00:42 -08:00
diced a26867c237 feat: mantine v6 -> v7 2023-12-02 15:35:50 -08:00
diced 3a01c6462f feat: updates & bigint 2023-11-30 22:10:34 -08:00
diced a3faee5b57 fix: maybe fix idk 2023-09-17 15:30:34 -07:00
diced 5d9e956b3c feat: arm64 image maybe 2023-09-17 15:19:15 -07:00
diced 2e46f69a56 fix: types with thumbnails 2023-09-17 15:01:33 -07:00
diced 01f94245c8 feat: metrics/stats page 2023-09-16 15:35:46 -07:00
diced 2a2ffaaffe feat: thumbnails 2023-08-30 20:08:49 -07:00
diced 734097b2c3 fix: actions lol 2023-08-29 22:10:43 -07:00
diced 09bc1dbc45 feat: idk why but why not lmao 2023-08-29 22:06:50 -07:00
diced 986914d841 fix: install dependencies in actions 2023-08-28 21:32:04 -07:00
diced 43cc1453e1 fix: forgot to change to pnpm from yarn bruh 2023-08-28 21:30:16 -07:00
diced 37f83c605b fix: fix github actions 2023-08-28 21:29:23 -07:00
diced 740d3db239 feat: reduce docker image size 2023-08-28 21:24:18 -07:00
diced 9d533b57bf feat: docker-compose.yml file 2023-08-27 22:28:27 -07:00
diced cadbd619a8 feat: docker & github actions 2023-08-20 14:57:25 -07:00
diced 2eb54b4618 fix: actions cache 2023-08-20 14:08:00 -07:00
diced 1f6beb0f54 fix: dependencies 2023-08-20 14:04:12 -07:00
diced d28e042048 feat: workflows 2023-08-20 13:55:10 -07:00
diced d5c5063597 fix: a lot fo stuff & a working build 2023-08-20 13:23:31 -07:00
diced 3ae24530f4 fix: expiring / max view deleting 2023-08-17 11:26:50 -07:00
diced b77082d99f fix: hide passkeys from other admins 2023-08-17 11:17:04 -07:00
diced 36ef6c38a2 fix: showing correct color swatch 2023-08-17 11:11:04 -07:00
diced 3317235a81 fix: redirect 2023-08-17 11:04:39 -07:00
diced 5a364ba8c3 fix: hang on logout 2023-08-17 11:02:03 -07:00
diced fcfa7b4a58 fix: black_dark theme 2023-08-17 10:59:46 -07:00
diced 4dc427e564 fix: colors for mfa options 2023-08-17 10:59:10 -07:00
diced 156f65048b feat: webauthn passkeys 2023-08-16 22:50:28 -07:00
diced 5e72450f39 feat: 2fa 2023-08-16 18:24:35 -07:00
diced 46b28663b7 feat: generator uploader 2023-08-14 22:04:31 -07:00
diced d2d1dbfc0f feat: implement settings.warnDeletion 2023-08-14 20:29:17 -07:00
diced 84cc111e73 fix: clean 2023-08-14 20:04:48 -07:00
diced bee6190169 fix: filtering on page count /api/user/files 2023-08-14 20:03:21 -07:00
diced 8f2d0bb3cd fix: actionicon variant in users 2023-08-09 23:30:21 -07:00
diced b6576c111d fix: spacing in file modal 2023-08-09 23:29:07 -07:00
diced a9b05e8a9c fix: bytes separator 2023-08-09 23:27:52 -07:00
diced 268eb2a851 feat: add a new stat 2023-08-09 23:26:27 -07:00
diced fae3ba8c5d feat: invites & user registration 2023-08-09 23:07:52 -07:00
diced 38d0228a03 feat: default domain in config 2023-08-08 23:33:15 -07:00
diced 1af2f3558c feat: remove gps & image compression 2023-08-08 23:20:05 -07:00
diced 53697d15bf feat: add check for folder x-zipline-folder 2023-08-08 15:14:20 -07:00
diced 8392b206de fix: clean up file search sql statement 2023-08-08 15:01:39 -07:00
diced 014f618863 feat: searching urls 2023-08-08 14:46:32 -07:00
diced 5d22291f7f fix: actionicon colors 2023-08-08 12:32:38 -07:00
diced 90b1385490 feat: default theme options in config & fallbacks 2023-08-08 12:11:03 -07:00
diced 111ab6f7ba fix: threshold 2023-08-08 00:39:51 -07:00
diced 7093c5f11e feat: themes 2023-08-08 00:29:45 -07:00
diced 453a66b1b4 feat: search file (pg_trgm) 2023-08-07 18:08:22 -07:00
diced b8cfebc4c8 feat: version api 2023-08-01 21:56:23 -07:00
diced 9b65f5936e feat: login background image 2023-07-29 20:57:20 -07:00
diced adfe00e2f2 feat: delete & maxviews job & bytes wrapper fixes 2023-07-29 11:42:24 -07:00
diced 3d374247df feat: scheduler & bytes wrapper 2023-07-28 19:47:28 -07:00
diced 60a5c478fe feat: folders 2023-07-22 22:12:22 -07:00
diced c85f0b9ae0 feat: selection saved across page warning 2023-07-21 17:33:54 -07:00
diced 66ead6e327 feat: make disableMediaPreview work 2023-07-21 17:31:50 -07:00
diced e82ef44b2a feat: bypass local login & block creation 2023-07-21 17:27:34 -07:00
diced 8b74b0b195 feat: oauth + authentik support (#372) 2023-07-21 11:47:46 -07:00
diced 485a1ae2e1 feat: config opt deleteonmaxviews 2023-07-20 21:59:20 -07:00
diced 7ca9a33ebb feat: views 2023-07-20 17:52:49 -07:00
diced fa30d082fd feat: shorten urls & root path support for everything ever 2023-07-20 17:42:30 -07:00
diced e0bfb0ddeb fix: change mimetypes to use "text/x-zipline-*" 2023-07-20 14:13:31 -07:00
diced e568b91774 fix: add in return http 2023-07-20 14:13:16 -07:00
diced 0670cbb586 featL settings, view routes, embeds, etc. 2023-07-20 12:35:08 -07:00
diced 5f295924b9 feat: show saved indicator for upload options 2023-07-18 21:23:04 -07:00
diced 1799dfaa5e fix: password paper 2023-07-18 11:40:19 -07:00
diced 21935e78da fix: weird styling stuff 2023-07-18 11:37:59 -07:00
diced 379914d7db feat: bulk transactions on file table 2023-07-17 22:08:06 -07:00
diced 0f1481c35a fix: make modals transition 2023-07-17 20:37:16 -07:00
diced db2dd8f1ea feat: upload options persisted 2023-07-17 20:30:33 -07:00
diced 30805e68f0 feat: render avatar in layout 2023-07-16 22:49:53 -07:00
diced 97b99a4830 feat: setup screen 2023-07-16 22:39:21 -07:00
diced ad4f56c163 fix: long file & not viewing ur own files 2023-07-16 21:51:19 -07:00
diced 0f94ad6488 feat: user roles 2023-07-16 21:35:23 -07:00
diced 48d04fffb7 feat: view users & their files 2023-07-09 21:33:07 -07:00
diced 30a861de07 feat: create users 2023-07-08 22:46:04 -07:00
diced 70da34db30 fix: logger name 2023-07-08 21:16:05 -07:00
diced 0bf7c503dd fix: styling on placeholders 2023-07-08 21:15:56 -07:00
diced 2c47066fd7 fix: actually view file 2023-07-08 21:11:53 -07:00
diced fe08fd40fc feat: add warning for large files 2023-07-08 21:10:08 -07:00
diced f4aad566c9 fix: paper for type table 2023-07-08 21:03:18 -07:00
diced c9b2961a29 fix: stats favorite files 2023-07-08 21:02:20 -07:00
diced a70be121fe fix: radius 2023-07-08 21:00:01 -07:00
diced c964bff4ac feat: files table & react ConfigProvider
* server side sorting / ordering
* ConfigProvider, useConfig (no need to pass config to every component)
2023-07-08 20:56:52 -07:00
diced d2ce56f5d3 feat: files view & integrate config into dashboard 2023-07-06 21:42:36 -07:00
diced fe01293429 feat: config opts, safe config for serversideprops 2023-07-05 22:02:39 -07:00
diced d8f7d024c3 feat: files view (wip) 2023-07-05 22:01:58 -07:00
diced f8477958a4 feat: new logger stuff 2023-07-04 22:56:21 -07:00
diced 404a061af9 feat: file buttons & passwords 2023-07-04 22:46:01 -07:00
diced 01e7b1f473 feat: uploading & rendering 2023-07-03 23:11:13 -07:00
diced c0fbbbb904 feat: more frontend 2023-07-01 23:04:03 -07:00
diced 75810ff90e feat: a frontend 2023-07-01 00:23:05 -07:00
diced 14da882840 feat: barebones upload 2023-06-29 14:38:31 -07:00
diced e4761c3f12 feat: stuffs 2023-06-27 22:39:39 -07:00
diced 8775ae2e29 fix: this is going to go away anyway idc 2023-06-25 10:23:05 -07:00
diced 9084808ad3 fix: add .env to gitignore 2023-06-24 00:28:33 -07:00
diced 10b5d32b99 fix: add .env to gitignore 2023-06-24 00:28:18 -07:00
diced 49e3bd5e9b feat: init 2023-06-24 00:24:47 -07:00
532 changed files with 60249 additions and 10249 deletions
+7 -4
View File
@@ -1,4 +1,7 @@
node_modules/
.next/
uploads/
.git/
.github
build
node_modules
uploads*
.env
.eslintcache
src/prisma
+1
View File
@@ -0,0 +1 @@
use flake . --no-pure-eval
-25
View File
@@ -1,25 +0,0 @@
{
"extends": ["next", "next/core-web-vitals"],
"rules": {
"indent": ["error", 2],
"linebreak-style": ["error", "unix"],
"quotes": ["error", "single"],
"semi": ["error", "always"],
"comma-dangle": ["error", "always-multiline"],
"jsx-quotes": ["error", "prefer-single"],
"react/prop-types": "off",
"react-hooks/rules-of-hooks": "off",
"react-hooks/exhaustive-deps": "off",
"react/jsx-uses-react": "warn",
"react/jsx-uses-vars": "warn",
"react/no-danger-with-children": "warn",
"react/no-deprecated": "warn",
"react/no-direct-mutation-state": "warn",
"react/no-is-mounted": "warn",
"react/no-typos": "error",
"react/react-in-jsx-scope": "error",
"react/require-render-return": "error",
"react/style-prop-object": "warn",
"@next/next/no-img-element": "off"
}
}
+1
View File
@@ -0,0 +1 @@
github: diced
+70
View File
@@ -0,0 +1,70 @@
name: Bug Report
description: Report a reproducible bug in Zipline
title: 'Bug: [short description of the issue]'
labels: ['bug']
body:
- type: textarea
id: what-happened
attributes:
label: Bug description
description: |
Describe in detail what you were doing and what happened.
Please include screenshots, logs, or error messages if possible, as they help diagnose the issue faster.
validations:
required: true
- type: dropdown
id: runtime-type
attributes:
label: How is Zipline being run?
description:
options:
- On docker (docker, docker compose, etc.)
- Built from source (running it through `pnpm start` or `node`, etc.)
- Other (please specify in the "Zipline Version" section)
validations:
required: true
- type: textarea
id: runtime-version
attributes:
label: Zipline Version
description: |
Provide the version of Zipline you are using:
- If version checking is enabled (it is by default): paste the response from `http://<domain>/api/version`
- If using docker (and can't do the above): specify the tag you are using (`latest`, `trunk`, or a tag digest)
- A simple version number (e.g. "4.2.1") may also suffice
placeholder: '4.2.1'
validations:
required: true
- type: dropdown
id: browsers
attributes:
label: If applicable, what browsers are you seeing this issue on?
multiple: true
options:
- Chromium based (Chrome, Brave, Edge, Opera, etc.)
- Firefox based (Firefox, Zen Browser, Waterfox, etc.)
- Safari (On macOS and/or iOS)
- Chromium based on Android/iOS
- Firefox based on Android/iOS
- Other (Please specify in the "Steps to Reproduce" section)
- type: textarea
id: zipline-logs
attributes:
label: Relevant Logs
description: |
Paste any relevant logs from Zipline or the browser (if applicable).
If logs don't look useful, you can enable debug mode by setting the environment variable `DEBUG=zipline` when starting Zipline.
Then reproduce the issue and copy the logs here.
**Note:** Debug logs may contain sensitive information.
- type: textarea
id: reproduction
attributes:
label: Steps to Reproduce
description: |
Please list the exact steps required to reproduce the issue.
Include any relevant configuration options, settings, or external services that may affect Ziplines functionality.
+11
View File
@@ -0,0 +1,11 @@
blank_issues_enabled: false
contact_links:
- name: Feature Request
url: https://github.com/diced/zipline/discussions/new?category=ideas&title=Your%20brief%20description%20here&labels=feature
about: Ask for a new feature
- name: Documentation
url: https://zipline.diced.sh
about: Maybe take a look a the docs?
- name: Zipline Discord
url: https://discord.gg/EAhCRfGxCF
about: Ask for help with anything related to Zipline!
+37 -20
View File
@@ -1,33 +1,50 @@
name: 'CI: Build'
name: 'Build'
on:
push:
branches: [ trunk ]
branches: [v4, trunk]
pull_request:
branches: [ trunk ]
branches: [v4, trunk]
workflow_dispatch:
jobs:
build:
runs-on: ubuntu-latest
strategy:
matrix:
node: [22.x, 24.x]
arch: [amd64, arm64]
runs-on: ubuntu-24.04${{ matrix.arch == 'arm64' && '-arm' || '' }}
steps:
- uses: actions/checkout@v2
- uses: actions/setup-node@v1
with:
node-version: '16.x'
- name: 'Restore dependency cache'
id: cache-restore
uses: actions/cache@v2
with:
path: node_modules
key: ${{ runner.os }}-node${{ matrix.node }}-${{ hashFiles('**/yarn.lock') }}
- uses: actions/checkout@v4
- name: Create mock config
run: echo -e "[core]\nsecret = '12345678'\ndatabase_url = 'postgres://postgres:postgres@postgres/postgres'\n[uploader]\nroute = '/u'\ndirectory = './uploads'\n[urls]\nroute = '/go'" > config.toml
- name: Use node@${{ matrix.node }}
uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node }}
- name: Install dependencies
if: steps.cache-restore.outputs.cache-hit != 'true'
run: yarn install
- uses: pnpm/action-setup@v4
with:
run_install: false
- name: Get pnpm store directory
shell: bash
id: pnpm-cache
run: |
echo "store_path=$(pnpm store path --silent)" >> $GITHUB_OUTPUT
- uses: actions/cache@v4
with:
path: |
${{ steps.pnpm-cache.outputs.store_path }}
key: ${{ runner.os }}-${{ matrix.arch }}-${{ matrix.node }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}-${{ hashFiles('**/*.js', '**/*.jsx', '**/*.ts', '**/*.tsx') }}
restore-keys: |
${{ runner.os }}-${{ matrix.arch }}-${{ matrix.node }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}-
- name: Install
run: pnpm install
- name: Build
run: yarn build
env:
ZIPLINE_BUILD: 'true'
run: pnpm build
+112
View File
@@ -0,0 +1,112 @@
name: 'Push Release Docker Images'
on:
push:
tags:
- 'v4.*.*'
workflow_dispatch:
jobs:
push:
strategy:
matrix:
arch: [amd64, arm64]
name: push release
runs-on: ubuntu-24.04${{ matrix.arch == 'arm64' && '-arm' || '' }}
steps:
- uses: actions/checkout@v4
- name: Get version
id: version
run: |
echo "zipline_version=$(jq .version package.json -r)" >> $GITHUB_OUTPUT
- name: Get commit sha
id: sha
run: |
echo "short_sha=$(git rev-parse --short HEAD)" >> $GITHUB_OUTPUT
- uses: docker/setup-buildx-action@v3
- uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- uses: docker/build-push-action@v6
with:
push: true
platforms: linux/${{ matrix.arch }}
cache-from: type=gha
cache-to: type=gha,mode=max
provenance: false
build-args: |
ZIPLINE_GIT_SHA=${{ steps.sha.outputs.short_sha }}
tags: |
ghcr.io/diced/zipline:${{ steps.version.outputs.zipline_version }}-${{ matrix.arch }}
ghcr.io/diced/zipline:${{ steps.version.outputs.zipline_version }}-${{ steps.sha.outputs.short_sha }}-${{ matrix.arch }}
${{ secrets.DOCKERHUB_USERNAME }}/zipline:${{ steps.version.outputs.zipline_version }}-${{ matrix.arch }}
${{ secrets.DOCKERHUB_USERNAME }}/zipline:${{ steps.version.outputs.zipline_version }}-${{ steps.sha.outputs.short_sha }}-${{ matrix.arch }}
amend-builds:
runs-on: ubuntu-24.04
needs: push
steps:
- uses: actions/checkout@v4
- name: Get version
id: version
run: |
echo "zipline_version=$(jq .version package.json -r)" >> $GITHUB_OUTPUT
- name: Get commit sha
id: sha
run: |
echo "short_sha=$(git rev-parse --short HEAD)" >> $GITHUB_OUTPUT
- uses: docker/setup-buildx-action@v3
- uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: pull images
run: |
docker pull --platform=linux/amd64 ghcr.io/diced/zipline:${{ steps.version.outputs.zipline_version }}-amd64
docker pull --platform=linux/arm64 ghcr.io/diced/zipline:${{ steps.version.outputs.zipline_version }}-arm64
docker pull --platform=linux/amd64 ${{ secrets.DOCKERHUB_USERNAME }}/zipline:${{ steps.version.outputs.zipline_version }}-amd64
docker pull --platform=linux/arm64 ${{ secrets.DOCKERHUB_USERNAME }}/zipline:${{ steps.version.outputs.zipline_version }}-arm64
- name: create manifests
run: |
docker manifest create ghcr.io/diced/zipline:${{ steps.version.outputs.zipline_version }} \
--amend ghcr.io/diced/zipline:${{ steps.version.outputs.zipline_version }}-amd64 \
--amend ghcr.io/diced/zipline:${{ steps.version.outputs.zipline_version }}-arm64 && \
docker manifest create ghcr.io/diced/zipline:latest \
--amend ghcr.io/diced/zipline:${{ steps.version.outputs.zipline_version }}-amd64 \
--amend ghcr.io/diced/zipline:${{ steps.version.outputs.zipline_version }}-arm64 && \
docker manifest create ${{ secrets.DOCKERHUB_USERNAME }}/zipline:${{ steps.version.outputs.zipline_version }} \
--amend ${{ secrets.DOCKERHUB_USERNAME }}/zipline:${{ steps.version.outputs.zipline_version }}-amd64 \
--amend ${{ secrets.DOCKERHUB_USERNAME }}/zipline:${{ steps.version.outputs.zipline_version }}-arm64 && \
docker manifest create ${{ secrets.DOCKERHUB_USERNAME }}/zipline:latest \
--amend ${{ secrets.DOCKERHUB_USERNAME }}/zipline:${{ steps.version.outputs.zipline_version }}-amd64 \
--amend ${{ secrets.DOCKERHUB_USERNAME }}/zipline:${{ steps.version.outputs.zipline_version }}-arm64
- name: push manifests
run: |
docker manifest push ghcr.io/diced/zipline:${{ steps.version.outputs.zipline_version }}
docker manifest push ghcr.io/diced/zipline:latest
docker manifest push ${{ secrets.DOCKERHUB_USERNAME }}/zipline:${{ steps.version.outputs.zipline_version }}
docker manifest push ${{ secrets.DOCKERHUB_USERNAME }}/zipline:latest
+92 -33
View File
@@ -1,45 +1,104 @@
name: 'CD: Push Docker Images'
name: 'Push Docker Images'
on:
push:
branches: [ trunk ]
paths:
- 'src/**'
- 'server/**'
- 'prisma/**'
- '.github/**'
branches: [v4, trunk]
workflow_dispatch:
jobs:
push_to_ghcr:
name: Push Image to GitHub Packages
runs-on: ubuntu-latest
steps:
- name: Check out the repo
uses: actions/checkout@v2
push:
strategy:
matrix:
arch: [amd64, arm64]
- name: Push to GitHub Packages
uses: docker/build-push-action@v1
name: push
runs-on: ubuntu-24.04${{ matrix.arch == 'arm64' && '-arm' || '' }}
steps:
- uses: actions/checkout@v4
- name: Get commit sha
id: sha
run: |
echo "short_sha=$(git rev-parse --short HEAD)" >> $GITHUB_OUTPUT
- uses: docker/setup-buildx-action@v3
- uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
registry: docker.pkg.github.com
repository: diced/zipline/zipline
dockerfile: Dockerfile
tag_with_ref: true
push_to_dockerhub:
name: Push Image to Docker Hub
runs-on: ubuntu-latest
steps:
- name: Check out the repo
uses: actions/checkout@v2
- name: Push to Docker Hub
uses: docker/build-push-action@v1
- uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
repository: diced/zipline
dockerfile: Dockerfile
tag_with_ref: true
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- uses: docker/build-push-action@v6
with:
push: true
platforms: linux/${{ matrix.arch }}
cache-from: type=gha
cache-to: type=gha,mode=max
provenance: false
build-args: |
ZIPLINE_GIT_SHA=${{ steps.sha.outputs.short_sha }}
tags: |
ghcr.io/diced/zipline:trunk-${{ matrix.arch }}
ghcr.io/diced/zipline:trunk-${{ steps.sha.outputs.short_sha }}-${{ matrix.arch }}
${{ secrets.DOCKERHUB_USERNAME }}/zipline:trunk-${{ matrix.arch }}
${{ secrets.DOCKERHUB_USERNAME }}/zipline:trunk-${{ steps.sha.outputs.short_sha }}-${{ matrix.arch }}
amend-builds:
runs-on: ubuntu-24.04
needs: push
steps:
- uses: actions/checkout@v4
- name: Get commit sha
id: sha
run: |
echo "short_sha=$(git rev-parse --short HEAD)" >> $GITHUB_OUTPUT
- uses: docker/setup-buildx-action@v3
- uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: pull images
run: |
docker pull --platform=linux/amd64 ghcr.io/diced/zipline:trunk-${{ steps.sha.outputs.short_sha }}-amd64
docker pull --platform=linux/arm64 ghcr.io/diced/zipline:trunk-${{ steps.sha.outputs.short_sha }}-arm64
docker pull --platform=linux/amd64 ${{ secrets.DOCKERHUB_USERNAME }}/zipline:trunk-${{ steps.sha.outputs.short_sha }}-amd64
docker pull --platform=linux/arm64 ${{ secrets.DOCKERHUB_USERNAME }}/zipline:trunk-${{ steps.sha.outputs.short_sha }}-arm64
- name: create manifests
run: |
docker manifest create ghcr.io/diced/zipline:trunk-${{ steps.sha.outputs.short_sha }} \
--amend ghcr.io/diced/zipline:trunk-${{ steps.sha.outputs.short_sha }}-amd64 \
--amend ghcr.io/diced/zipline:trunk-${{ steps.sha.outputs.short_sha }}-arm64 && \
docker manifest create ghcr.io/diced/zipline:trunk \
--amend ghcr.io/diced/zipline:trunk-${{ steps.sha.outputs.short_sha }}-amd64 \
--amend ghcr.io/diced/zipline:trunk-${{ steps.sha.outputs.short_sha }}-arm64
docker manifest create ghcr.io/diced/zipline:v4 \
--amend ghcr.io/diced/zipline:trunk-${{ steps.sha.outputs.short_sha }}-amd64 \
--amend ghcr.io/diced/zipline:trunk-${{ steps.sha.outputs.short_sha }}-arm64
docker manifest create ${{ secrets.DOCKERHUB_USERNAME }}/zipline:trunk \
--amend ${{ secrets.DOCKERHUB_USERNAME }}/zipline:trunk-${{ steps.sha.outputs.short_sha }}-amd64 \
--amend ${{ secrets.DOCKERHUB_USERNAME }}/zipline:trunk-${{ steps.sha.outputs.short_sha }}-arm64
docker manifest create ${{ secrets.DOCKERHUB_USERNAME }}/zipline:trunk-${{ steps.sha.outputs.short_sha }} \
--amend ${{ secrets.DOCKERHUB_USERNAME }}/zipline:trunk-${{ steps.sha.outputs.short_sha }}-amd64 \
--amend ${{ secrets.DOCKERHUB_USERNAME }}/zipline:trunk-${{ steps.sha.outputs.short_sha }}-arm64
- name: push manifests
run: |
docker manifest push ghcr.io/diced/zipline:trunk-${{ steps.sha.outputs.short_sha }}
docker manifest push ghcr.io/diced/zipline:trunk
docker manifest push ghcr.io/diced/zipline:v4
docker manifest push ${{ secrets.DOCKERHUB_USERNAME }}/zipline:trunk
docker manifest push ${{ secrets.DOCKERHUB_USERNAME }}/zipline:trunk-${{ steps.sha.outputs.short_sha }}
+99
View File
@@ -0,0 +1,99 @@
name: Generate OpenAPI Spec
on:
push:
branches: [v4, trunk]
pull_request:
branches: [v4, trunk]
workflow_dispatch:
jobs:
gen-openapi:
strategy:
matrix:
node: [24.x]
arch: [amd64]
runs-on: ubuntu-24.04
services:
postgres:
image: postgres:16
ports:
- 5432:5432
env:
POSTGRES_USER: zipline
POSTGRES_PASSWORD: zipline
POSTGRES_DB: zipline
options: >-
--health-cmd="pg_isready -U zipline -d zipline"
--health-interval=5s
--health-timeout=5s
--health-retries=10
steps:
- uses: actions/checkout@v4
- name: Use node@${{ matrix.node }}
uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node }}
- uses: pnpm/action-setup@v4
with:
run_install: false
- name: Get pnpm store directory
shell: bash
id: pnpm-cache
run: |
echo "store_path=$(pnpm store path --silent)" >> $GITHUB_OUTPUT
- uses: actions/cache@v4
with:
path: |
${{ steps.pnpm-cache.outputs.store_path }}
key: ${{ runner.os }}-${{ matrix.arch }}-${{ matrix.node }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}-${{ hashFiles('**/*.js', '**/*.jsx', '**/*.ts', '**/*.tsx') }}
restore-keys: |
${{ runner.os }}-${{ matrix.arch }}-${{ matrix.node }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}-
- name: Install
run: pnpm install
- name: Build
env:
ZIPLINE_BUILD: 'true'
run: pnpm build
- name: Generate secret
id: secret
run: |
SECRET=$(openssl rand -base64 48 | tr -dc 'a-zA-Z0-9')
echo "secret=$SECRET" >> $GITHUB_OUTPUT
- name: Wait for Postgres
run: |
until pg_isready -h localhost -p 5432 -U zipline; do
echo "Waiting for postgres..."
sleep 2
done
- name: Run generator
env:
DATABASE_URL: postgres://zipline:zipline@localhost:5432/zipline
CORE_SECRET: ${{ steps.secret.outputs.secret }}
NODE_ENV: production
run: pnpm openapi
- name: Verify openapi.json exists
run: |
if [ ! -f "./openapi.json" ]; then
echo "openapi.json not found"
exit 1
fi
- name: Upload openapi.json
uses: actions/upload-artifact@v4
with:
name: openapi-json
path: ./openapi.json
+25 -11
View File
@@ -5,20 +5,22 @@
/.pnp
.pnp.js
# yarn
.yarn/*
!.yarn/releases
!.yarn/plugins
# testing
/coverage
# next.js
/.next/
/out/
# production
/build
build/
# misc
.DS_Store
*.pem
.idea
.vscode
# debug
npm-debug.log*
@@ -26,14 +28,26 @@ yarn-debug.log*
yarn-error.log*
# local env files
.env.local
.env.development.local
.env.test.local
.env.production.local
.env*
# vercel
.vercel
# typescript
*.tsbuildinfo
# eslint
.eslintcache
# nix dev env
!.envrc
.direnv
.devenv
# zipline
config.toml
uploads/
uploads*/
*.crt
*.key
src/prisma
.memory.log*
openapi.json
+1
View File
@@ -0,0 +1 @@
pnpm-lock.yaml
+53 -35
View File
@@ -1,46 +1,64 @@
FROM node:16-alpine AS deps
WORKDIR /build
FROM node:22-alpine3.21 AS base
COPY package.json yarn.lock ./
ENV PNPM_HOME="/pnpm"
ENV PATH="$PNPM_HOME:$PATH"
RUN apk add --no-cache libc6-compat
RUN yarn install --frozen-lockfile
RUN corepack enable
FROM node:16-alpine AS builder
WORKDIR /build
RUN apk add --no-cache ffmpeg tzdata
COPY --from=deps /build/node_modules ./node_modules
COPY src ./src
COPY server ./server
COPY scripts ./scripts
COPY prisma ./prisma
COPY package.json yarn.lock next.config.js next-env.d.ts zip-env.d.ts tsconfig.json ./
ENV ZIPLINE_DOCKER_BUILD 1
ENV NEXT_TELEMETRY_DISABLED 1
RUN yarn build
FROM node:16-alpine AS runner
WORKDIR /zipline
ENV NODE_ENV production
ENV NEXT_TELEMETRY_DISABLED 1
COPY prisma ./prisma
COPY package.json .
COPY pnpm-lock.yaml .
RUN addgroup --system --gid 1001 zipline
RUN adduser --system --uid 1001 zipline
FROM base AS deps
RUN --mount=type=cache,id=pnpm,target=/pnpm/store pnpm install --prod --frozen-lockfile
COPY --from=builder --chown=zipline:zipline /build/.next ./.next
COPY --from=builder --chown=zipline:zipline /build/node_modules ./node_modules
FROM base AS builder
RUN --mount=type=cache,id=pnpm,target=/pnpm/store pnpm install --frozen-lockfile
COPY --from=builder /build/next.config.js ./next.config.js
COPY --from=builder /build/src ./src
COPY --from=builder /build/server ./server
COPY --from=builder /build/scripts ./scripts
COPY --from=builder /build/prisma ./prisma
COPY --from=builder /build/tsconfig.json ./tsconfig.json
COPY --from=builder /build/package.json ./package.json
COPY src ./src
COPY .gitignore ./.gitignore
USER zipline
COPY postcss.config.cjs ./postcss.config.cjs
COPY prettier.config.cjs ./prettier.config.cjs
COPY eslint.config.mjs ./eslint.config.mjs
COPY vite.config.ts ./vite.config.ts
COPY tsup.config.ts ./tsup.config.ts
COPY tsconfig.json ./tsconfig.json
COPY mimes.json ./mimes.json
COPY code.json ./code.json
COPY vite-env.d.ts ./vite-env.d.ts
COPY scripts ./scripts
CMD ["node", "server"]
RUN ZIPLINE_BUILD=true pnpm run build
FROM base
COPY --from=deps /zipline/node_modules ./node_modules
COPY --from=builder /zipline/build ./build
COPY --from=builder /zipline/mimes.json ./mimes.json
COPY --from=builder /zipline/code.json ./code.json
RUN pnpm prisma generate
# clean
RUN rm -rf /tmp/* /root/*
ENV NODE_ENV=production
ENV ZIPLINE_ROOT=/zipline
ARG ZIPLINE_GIT_SHA
ENV ZIPLINE_GIT_SHA=${ZIPLINE_GIT_SHA:-"unknown"}
# add scripts
COPY docker/entrypoint.sh /zipline/entrypoint
COPY docker/ziplinectl.sh /zipline/ziplinectl
RUN ln -s /zipline/ziplinectl /usr/local/bin/ziplinectl
ENTRYPOINT ["/zipline/entrypoint"]
+1 -1
View File
@@ -1,6 +1,6 @@
MIT License
Copyright (c) 2021 dicedtomato
Copyright (c) 2023 dicedtomato
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
+324 -25
View File
@@ -1,34 +1,333 @@
<div align="center">
<img src="https://raw.githubusercontent.com/diced/zipline/trunk/public/zipline_small.png"/>
Zipline is a ShareX/file upload server that is easy to use, packed with features and can be setup in one command!
![Build](https://img.shields.io/github/workflow/status/diced/zipline/CD:%20Push%20Docker%20Images?logo=github&style=flat-square)
![Stars](https://img.shields.io/github/stars/diced/zipline?logo=github&style=flat-square)
![Version](https://img.shields.io/github/package-json/v/diced/zipline?logo=git&logoColor=white&style=flat-square)
![GitHub last commit (branch)](https://img.shields.io/github/last-commit/diced/zipline/trunk?logo=git&logoColor=white&style=flat-square)
[![Discord](https://img.shields.io/discord/729771078196527176?color=%23777ed3&label=discord&logo=discord&logoColor=white&style=flat-square)](https://discord.gg/EAhCRfGxCF)
The next generation ShareX / File upload server
![Stars](https://img.shields.io/github/stars/diced/zipline?logo=github&style=for-the-badge)
![Version](https://img.shields.io/github/package-json/v/diced/zipline?logo=git&logoColor=white&style=for-the-badge)
![GitHub last commit (branch)](https://img.shields.io/github/last-commit/diced/zipline/trunk?logo=git&logoColor=white&style=for-the-badge)
[![Discord](https://img.shields.io/discord/729771078196527176?color=%23777ed3&label=discord&logo=discord&logoColor=white&style=for-the-badge)](https://discord.gg/EAhCRfGxCF)
![Build](https://img.shields.io/github/actions/workflow/status/diced/zipline/build.yml?logo=github&style=for-the-badge&branch=trunk)
Documentation: [zipline.diced.sh](https://zipline.diced.sh)
</div>
## Features
- Configurable
- Fast
- Built with Next.js & React
- Token protected uploading
- Image uploading
- Setup Quickly: [Get Started with Docker](https://zipline.diced.sh/docs/get-started/docker)
- Configure
- Upload any file
- Folders
- Tags
- URL shortening
- Text uploading
- URL Formats (uuid, dates, random alphanumeric, original name, zws)
- Discord embeds (OG metadata)
- Gallery viewer, and multiple file format support
- Easy setup instructions on [docs](https://zipline.diced.tech/) (One command install `docker-compose up -d`)
- Embeds
- Discord Webhooks
- HTTP Webhooks
- OAuth2
- 2FA
- Passkeys
- Password Protection
- Image Compression
- Video Thumbnails
- API
- PWA
- Partial Uploads
- Invites
- Quotas
- Custom Themes
- ... and more!
## Installing
[See how to install here](https://zipline.diced.tech/docs/get-started)
# Usage
## Configuration
[See how to configure here](https://zipline.diced.tech/docs/config/overview)
Visit [the docs](https://zipline.diced.sh/docs/get-started/docker) for a more in-depth guide on how to get started.
## Theming
[See how to theme here](https://zipline.diced.tech/docs/themes/reference)
## Install and Run with Docker
This is the recommended way to run Zipline:
```yml
services:
postgresql:
image: postgres:16
restart: unless-stopped
env_file:
- .env
environment:
POSTGRES_USER: ${POSTGRESQL_USER:-zipline}
POSTGRES_PASSWORD: ${POSTGRESQL_PASSWORD:?POSTGRESSQL_PASSWORD is required}
POSTGRES_DB: ${POSTGRESQL_DB:-zipline}
volumes:
- pgdata:/var/lib/postgresql/data
healthcheck:
test: ['CMD', 'pg_isready', '-U', 'zipline']
interval: 10s
timeout: 5s
retries: 5
zipline:
image: ghcr.io/diced/zipline
ports:
- '3000:3000'
env_file:
- .env
environment:
- DATABASE_URL=postgres://${POSTGRESQL_USER:-zipline}:${POSTGRESQL_PASSWORD}@postgresql:5432/${POSTGRESQL_DB:-zipline}
depends_on:
postgresql:
condition: service_healthy
volumes:
- './uploads:/zipline/uploads'
- './public:/zipline/public'
- './themes:/zipline/themes'
healthcheck:
test: ['CMD', 'wget', '-q', '--spider', 'http://localhost:3000/api/healthcheck']
interval: 15s
timeout: 2s
retries: 2
volumes:
pgdata:
```
### Volumes
- `./uploads` - The folder where all the user uploads are stored (the default is `./uploads`)
- `./public` - The folder where all the public assets are stored (must mount to `/zipline/public`)
- `./themes` - The folder where all the custom themes are stored (must mount to `/zipline/themes`)
### Generating Secrets
```bash
echo "POSTGRESQL_PASSWORD=$(openssl rand -base64 42 | tr -dc A-Za-z0-9 | cut -c -32 | tr -d '\n')" > .env
echo "CORE_SECRET=$(openssl rand -base64 42 | tr -dc A-Za-z0-9 | cut -c -32 | tr -d '\n')" >> .env
```
Without the `CORE_SECRET` environment variable, Zipline will not start.
### Changing where uploads are stored
By default, Zipline will default to the `./uploads` folder, which is also reflected in the `docker-compose.yml` above. If you want to change this, you can set the `DATASOURCE_LOCAL_DIRECTORY` environment variable to a different path.
```bash
DATASOURCE_LOCAL_DIRECTORY=/path/to/your/local/files
# or relative to the working directory
DATASOURCE_LOCAL_DIRECTORY=./relative/path/to/files
```
> [!NOTE]
> Remember to change volume mappings in the docker-compose.yml file if you change this.
### Changing the port and hostname
By default, Zipline binds to `0.0.0.0:3000`, which is also reflected in the `docker-compose.yml` above. If you want to change this, you can set the `CORE_PORT` and `CORE_HOSTNAME` environment variables to a different port and hostname.
```bash
CORE_PORT=80
CORE_HOSTNAME=localhost
```
> [!NOTE]
> If you change the port, you will need to update the `ports` section in the `docker-compose.yml` file.
### Using S3
If you want to use S3 instead of the local filesystem, you can set the following environment variables:
```bash
DATASOURCE_TYPE=s3
DATASOURCE_S3_ACCESS_KEY_ID=access_key_id
DATASOURCE_S3_SECRET_ACCESS_KEY=secret
DATASOURCE_S3_BUCKET=zipline
DATASOURCE_S3_REGION=us-west-2
```
For more information, like other providers, see the [docs](https://zipline.diced.sh/docs/config/datasource#s3-datasource).
### Starting Zipline
Simply run the following command to start the server:
```bash
docker compose up -d
```
You should be able to access the website at `http://localhost:3000` or the port you specified.
## Manual Install
See [docs](https://zipline.diced.sh/docs/get-started/source) for more information.
# Migrating from v3
Zipline v4 was a complete rewrite, and as such, there is no upgrade path from v3 to v4. You will need to export your data from v3 and import it into v4. This process is made easier by the fact that v4 has a built-in importer to import data from v3.
See [migration](https://zipline.diced.sh/docs/migrate) for more information.
# Contributing
Contributions of any kind are welcome, whether they are bug reports, pull requests, or feature requests.
## Bug Reports
Create an issue on GitHub and use the template, please include the following (if one of them is not applicable to the issue then it's not needed):
- The steps to reproduce the bug
- Logs of Zipline
- The version of Zipline, and whether or not you are using Docker (include the image digest/tag if possible)
- Your OS & Browser including server OS
- What you were expecting to see
- How it can be fixed (if you know)
## Feature Requests
Create a discussion on GitHub, and please include the following:
- Brief explanation of your feature in the title (very brief)
- How it would work (be detailed)
## Pull Requests
Create a pull request on GitHub. If your PR does not pass the action checks, then please fix the errors. If your PR was submitted before a release, and I have pushed a new release, please make sure to update your PR to reflect any changes, usually this is handled by GitHub.
### Development
Here's how to setup Zipline for development
#### Nix
If you have [Nix](https://nixos.org) and [direnv](https://direnv.net/) installed, you can simply cd into the cloned directory and run the following command:
```bash
direnv allow
```
After doing so, your shell will be setup for development.
If you aren't using direnv, you can run the following command to enter the nix shell:
```bash
nix develop --no-pure-eval
```
Useful commands regarding the postgres server:
| Command | Description |
| --------------- | --------------------------------------------- |
| `pgup` | Starts the postgres server in the background. |
| `pg_ctl status` | See if the postgres server is running |
| `minioup` | Start a Minio server for testing S3 |
| `downall` | Stops any running postgres or minio service. |
After familiarizing yourself with the environment, you can continue below (skipping the prerequisites since they are already installed).
#### Prerequisites
- nodejs (lts -> 20.x, 22.x)
- pnpm (10.x)
- a postgresql server
#### Setup
You should probably use a `.env` file to manage your environment variables, here is an example .env file with every available environment variable:
```bash
DEBUG=zipline
# required
CORE_SECRET="a secret that is 32 characters long"
# required
DATABASE_URL="postgresql://postgres:postgres@localhost:5432/zipline?schema=public"
# these are optional
CORE_PORT=3000
CORE_HOSTNAME=0.0.0.0
# one of these is required
DATASOURCE_TYPE="local"
# DATASOURCE_TYPE="s3"
# if DATASOURCE_TYPE=local
DATASOURCE_LOCAL_DIRECTORY="/path/to/your/local/files"
# if DATASOURCE_TYPE=s3
# DATASOURCE_S3_ACCESS_KEY_ID="your-access-key-id"
# DATASOURCE_S3_SECRET_ACCESS_KEY="your-secret-access-key"
# DATASOURCE_S3_REGION="your-region"
# DATASOURCE_S3_BUCKET="your-bucket"
# DATASOURCE_S3_ENDPOINT="your-endpoint"
# ^ if using a custom endpoint other than aws s3
```
Install dependencies:
```bash
pnpm install
```
Finally you may start the development server:
```bash
pnpm dev
```
If you wish to build the production version of Zipline, you can run the following command:
```bash
pnpm build
```
And to run the production version of Zipline:
```bash
pnpm start
```
#### Making changes to the database schema
Zipline uses [prisma](https://www.prisma.io/) as its ORM, and as such, you will need to use the prisma CLI to facilitate any changes to the database schema.
Once you have made a change to `prisma.schema`, you can run the script `db:migrate` to generate a migration file. This script doesn't apply the migration, as Zipline handles applying migrations itself on startup.
```bash
pnpm db:migrate
```
If you wish to push changes to the database without generating a migration file, you can run the script `db:prototype`. This is only recommended for testing purposes, and should not be used in production.
```bash
pnpm db:prototype
```
#### Linting and Formatting
Zipline will fail to build unless the code is properly formatted and linted. To format the code, you can run the following command:
```bash
pnpm validate
```
#### Testing `zipline-ctl`
To build the ctl, you can run the following command:
```bash
pnpm build:server
```
then run any command you want
```bash
pnpm ctl help
```
# Documentation
Documentation is located at [zipline.diced.sh](https://zipline.diced.sh) and the source is located at [github.com/diced/zipline-docs](https://github.com/diced/zipline-docs).
# Security
Security issues are taken seriously, and should be reported via [GitHub Advisories](https://github.com/diced/zipline/security/advisories). For more information see the [security policy](SECURITY.md).
+5 -2
View File
@@ -4,9 +4,12 @@
| Version | Supported |
| ------- | ------------------ |
| 3.2.x | :white_check_mark: |
| 4.4.x | :white_check_mark: |
| < 3 | :x: |
| < 2 | :x: |
## Reporting a Vulnerability
Report a Vulnerability by issuing a bug report, with exact details with how the vulnerability happened, what "exploits" can happen, and possible fixes (optional). Vulnerability reports are treated with high priority and will be resolved most of the time quickly.
Report a Vulnerability [here](https://github.com/diced/zipline/security/advisories) (click Report a Vulnerability). Please include exact details with how to reproduce the vulnerability, and if possible, a proof of concept that demonstrates the vulnerability.
<- Go [back](README.md#SECURITY)
+212
View File
@@ -0,0 +1,212 @@
[
{
"ext": "html",
"mime": "text/x-zipline-html",
"name": "HTML"
},
{
"ext": "css",
"mime": "text/x-zipline-css",
"name": "CSS"
},
{
"ext": "cpp",
"mime": "text/x-zipline-c++src",
"name": "C++"
},
{
"ext": "js",
"mime": "text/x-zipline-javascript",
"name": "JavaScript"
},
{
"ext": "py",
"mime": "text/x-zipline-python",
"name": "Python"
},
{
"ext": "rb",
"mime": "text/x-zipline-ruby",
"name": "Ruby"
},
{
"ext": "java",
"mime": "text/x-zipline-java",
"name": "Java"
},
{
"ext": "md",
"mime": "text/x-zipline-markdown",
"name": "Markdown"
},
{
"ext": "c",
"mime": "text/x-zipline-csrc",
"name": "C"
},
{
"ext": "php",
"mime": "text/x-zipline-httpd-php",
"name": "PHP"
},
{
"ext": "sass",
"mime": "text/x-zipline-sass",
"name": "Sass"
},
{
"ext": "scss",
"mime": "text/x-zipline-scss",
"name": "SCSS"
},
{
"ext": "swift",
"mime": "text/x-zipline-swift",
"name": "Swift"
},
{
"ext": "ts",
"mime": "text/x-zipline-typescript",
"name": "TypeScript"
},
{
"ext": "go",
"mime": "text/x-zipline-go",
"name": "Go"
},
{
"ext": "rs",
"mime": "text/x-zipline-rustsrc",
"name": "Rust"
},
{
"ext": "sh",
"mime": "text/x-zipline-sh",
"name": "Bash"
},
{
"ext": "json",
"mime": "text/x-zipline-json",
"name": "JSON"
},
{
"ext": "ps1",
"mime": "text/x-zipline-powershell",
"name": "PowerShell"
},
{
"ext": "sql",
"mime": "text/x-zipline-sql",
"name": "SQL"
},
{
"ext": "yaml",
"mime": "text/x-zipline-yaml",
"name": "YAML"
},
{
"ext": "dockerfile",
"mime": "text/x-zipline-dockerfile",
"name": "Dockerfile"
},
{
"ext": "lua",
"mime": "text/x-zipline-lua",
"name": "Lua"
},
{
"ext": "conf",
"mime": "text/x-zipline-nginx-conf",
"name": "NGINX Config File"
},
{
"ext": "pl",
"mime": "text/x-zipline-perl",
"name": "Perl"
},
{
"ext": "r",
"mime": "text/x-zipline-rsrc",
"name": "R"
},
{
"ext": "scala",
"mime": "text/x-zipline-scala",
"name": "Scala"
},
{
"ext": "groovy",
"mime": "text/x-zipline-groovy",
"name": "Groovy"
},
{
"ext": "kt",
"mime": "text/x-zipline-kotlin",
"name": "Kotlin"
},
{
"ext": "hs",
"mime": "text/x-zipline-haskell",
"name": "Haskell"
},
{
"ext": "ex",
"mime": "text/x-zipline-elixir",
"name": "Elixir"
},
{
"ext": "vim",
"mime": "text/x-zipline-vim",
"name": "Vim"
},
{
"ext": "m",
"mime": "text/x-zipline-matlab",
"name": "MATLAB"
},
{
"ext": "dart",
"mime": "text/x-zipline-dart",
"name": "Dart"
},
{
"ext": "hbs",
"mime": "text/x-zipline-handlebars-template",
"name": "Handlebars"
},
{
"ext": "hcl",
"mime": "text/x-zipline-hcl",
"name": "HCL"
},
{
"ext": "http",
"mime": "text/x-zipline-http",
"name": "HTTP"
},
{
"ext": "ini",
"mime": "text/x-zipline-ini",
"name": "INI"
},
{
"ext": "jsx",
"mime": "text/x-zipline-jsx",
"name": "JSX"
},
{
"ext": "coffee",
"mime": "text/x-zipline-coffeescript",
"name": "CoffeeScript"
},
{
"ext": "tex",
"mime": "text/x-zipline-latex",
"name": "LaTeX (KaTeX)"
},
{
"name": "Plain Text",
"mime": "text/x-zipline-plain",
"ext": "txt"
}
]
-19
View File
@@ -1,19 +0,0 @@
[core]
secure = true
secret = 'some secret'
host = '0.0.0.0'
port = 3000
database_url = 'postgres://postgres:postgres@postgres/postgres'
[urls]
route = '/go'
length = 6
[uploader]
route = '/u'
embed_route = '/a'
length = 6
directory = './uploads'
user_limit = 104900000 # 100mb
admin_limit = 104900000 # 100mb
disabled_extentions = ['jpg']
+18 -28
View File
@@ -1,15 +1,15 @@
version: '3'
services:
postgres:
image: postgres
environment:
image: postgres:16
restart: unless-stopped
environment:
- POSTGRES_USER=postgres
- POSTGRES_PASSWORD=postgres
- POSTGRES_DATABASE=postgres
volumes:
- pg_data:/var/lib/postgresql/data
- POSTGRES_DATABASE=postgres2
volumes:
- pgdata:/var/lib/postgresql/data
healthcheck:
test: ['CMD-SHELL', 'pg_isready -U postgres']
test: ['CMD', 'pg_isready', '-U', 'postgres']
interval: 10s
timeout: 5s
retries: 5
@@ -20,27 +20,17 @@ services:
dockerfile: Dockerfile
ports:
- '3000:3000'
restart: unless-stopped
environment:
- SECURE=false
- SECRET=changethis
- HOST=0.0.0.0
- PORT=3000
- DATABASE_URL=postgresql://postgres:postgres@postgres/postgres/
- UPLOADER_ROUTE=/u
- UPLOADER_EMBED_ROUTE=/a
- UPLOADER_LENGTH=6
- UPLOADER_DIRECTORY=./uploads
- UPLOADER_ADMIN_LIMIT=104900000
- UPLOADER_USER_LIMIT=104900000
- UPLOADER_DISABLED_EXTS=
- URLS_ROUTE=/go
- URLS_LENGTH=6
volumes:
- '$PWD/uploads:/zipline/uploads'
- '$PWD/public:/zipline/public'
env_file:
- .env
environment:
- DATABASE_URL=postgres://postgres:postgres@postgres/postgres2
- CORE_HOSTNAME=0.0.0.0
depends_on:
- 'postgres'
- postgres
volumes:
- './uploads:/zipline/uploads'
- './public:/zipline/public'
- './themes:/zipline/themes'
volumes:
pg_data:
pgdata:
+30 -32
View File
@@ -1,44 +1,42 @@
version: '3'
services:
postgres:
image: postgres
environment:
- POSTGRES_USER=postgres
- POSTGRES_PASSWORD=postgres
- POSTGRES_DATABASE=postgres
volumes:
- pg_data:/var/lib/postgresql/data
postgresql:
image: postgres:16
restart: unless-stopped
env_file:
- .env
environment:
POSTGRES_USER: ${POSTGRESQL_USER:-zipline}
POSTGRES_PASSWORD: ${POSTGRESQL_PASSWORD:?POSTGRESQL_PASSWORD is required}
POSTGRES_DB: ${POSTGRESQL_DB:-zipline}
volumes:
- pgdata:/var/lib/postgresql/data
healthcheck:
test: ['CMD-SHELL', 'pg_isready -U postgres']
test: ['CMD', 'pg_isready', '-U', 'zipline']
interval: 10s
timeout: 5s
retries: 5
zipline:
image: ghcr.io/diced/zipline/zipline:trunk
image: ghcr.io/diced/zipline:latest
restart: unless-stopped
ports:
- '3000:3000'
restart: unless-stopped
environment:
- SECURE=false
- SECRET=changethis
- HOST=0.0.0.0
- PORT=3000
- DATABASE_URL=postgresql://postgres:postgres@postgres/postgres/
- UPLOADER_ROUTE=/u
- UPLOADER_EMBED_ROUTE=/a
- UPLOADER_LENGTH=6
- UPLOADER_DIRECTORY=./uploads
- UPLOADER_ADMIN_LIMIT=104900000
- UPLOADER_USER_LIMIT=104900000
- UPLOADER_DISABLED_EXTS=
- URLS_ROUTE=/go
- URLS_LENGTH=6
volumes:
- '$PWD/uploads:/zipline/uploads'
- '$PWD/public:/zipline/public'
env_file:
- .env
environment:
- DATABASE_URL=postgres://${POSTGRESQL_USER:-zipline}:${POSTGRESQL_PASSWORD}@postgresql:5432/${POSTGRESQL_DB:-zipline}
depends_on:
- 'postgres'
postgresql:
condition: service_healthy
volumes:
- './uploads:/zipline/uploads'
- './public:/zipline/public'
- './themes:/zipline/themes'
healthcheck:
test: ['CMD', 'wget', '-q', '--spider', 'http://0.0.0.0:3000/api/healthcheck']
interval: 15s
timeout: 2s
retries: 2
volumes:
pg_data:
pgdata:
+5
View File
@@ -0,0 +1,5 @@
#!/usr/bin/env sh
set -e
cd ${ZIPLINE_ROOT:-/zipline}
exec node --enable-source-maps build/server
+6
View File
@@ -0,0 +1,6 @@
#!/usr/bin/env sh
set -e
cd ${ZIPLINE_ROOT:-/zipline}
exec node --enable-source-maps build/ctl "$@"
+107
View File
@@ -0,0 +1,107 @@
import path from 'node:path';
import { fileURLToPath } from 'node:url';
import fs from 'node:fs';
import tseslint from 'typescript-eslint';
import reactPlugin from 'eslint-plugin-react';
import reactHooksPlugin from 'eslint-plugin-react-hooks';
import reactRefreshPlugin from 'eslint-plugin-react-refresh';
import jsxA11yPlugin from 'eslint-plugin-jsx-a11y';
import prettier from 'eslint-plugin-prettier';
import prettierConfig from 'eslint-config-prettier';
import unusedImports from 'eslint-plugin-unused-imports';
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
const gitignorePath = path.resolve(__dirname, '.gitignore');
const gitignoreContent = fs.readFileSync(gitignorePath, 'utf8');
const gitignorePatterns = gitignoreContent
.split('\n')
.filter((line) => line.trim() && !line.startsWith('#'))
.map((pattern) => pattern.trim());
import { defineConfig } from 'eslint/config';
export default defineConfig(
tseslint.configs.recommended,
jsxA11yPlugin.flatConfigs.recommended,
reactPlugin.configs.flat.recommended,
reactHooksPlugin.configs.flat.recommended,
reactRefreshPlugin.configs.vite,
{ ignores: gitignorePatterns },
{
files: ['**/*.{js,mjs,cjs,ts,tsx}'],
languageOptions: {
ecmaVersion: 'latest',
sourceType: 'module',
parserOptions: {
ecmaFeatures: { jsx: true },
},
},
plugins: {
react: reactPlugin,
'react-hooks': reactHooksPlugin,
prettier,
'unused-imports': unusedImports,
},
rules: {
...prettierConfig.rules,
'prettier/prettier': ['error', {}, { fileInfoOptions: { withNodeModules: false } }],
'linebreak-style': ['error', 'unix'],
quotes: ['error', 'single', { avoidEscape: true }],
semi: ['error', 'always'],
'jsx-quotes': ['error', 'prefer-single'],
indent: 'off',
'react/prop-types': 'off',
'react-hooks/rules-of-hooks': 'off',
'react-hooks/exhaustive-deps': 'off',
'react-hooks/set-state-in-effect': 'warn',
'react-refresh/only-export-components': 'off',
'react/jsx-uses-react': 'warn',
'react/jsx-uses-vars': 'warn',
'react/no-danger-with-children': 'warn',
'react/no-deprecated': 'warn',
'react/no-direct-mutation-state': 'warn',
'react/no-is-mounted': 'warn',
'react/no-typos': 'error',
'react/react-in-jsx-scope': 'off',
'react/require-render-return': 'error',
'react/style-prop-object': 'warn',
'react/display-name': 'off',
'jsx-a11y/alt-text': 'off',
'jsx-a11y/no-autofocus': 'off',
'jsx-a11y/click-events-have-key-events': 'off',
'jsx-a11y/no-static-element-interactions': 'off',
'no-unused-vars': 'off',
'@typescript-eslint/no-unused-vars': 'off',
'unused-imports/no-unused-imports': 'error',
'unused-imports/no-unused-vars': [
'warn',
{ vars: 'all', varsIgnorePattern: '^_', args: 'after-used', argsIgnorePattern: '^_' },
],
'@typescript-eslint/ban-ts-comment': 'off',
'@typescript-eslint/no-explicit-any': 'off',
'@typescript-eslint/no-unused-expressions': 'off',
},
settings: {
react: { version: 'detect' },
},
},
);
Generated
+254
View File
@@ -0,0 +1,254 @@
{
"nodes": {
"cachix": {
"inputs": {
"devenv": [
"devenv"
],
"flake-compat": [
"devenv"
],
"git-hooks": [
"devenv",
"git-hooks"
],
"nixpkgs": [
"devenv",
"nixpkgs"
]
},
"locked": {
"lastModified": 1748883665,
"narHash": "sha256-R0W7uAg+BLoHjMRMQ8+oiSbTq8nkGz5RDpQ+ZfxxP3A=",
"owner": "cachix",
"repo": "cachix",
"rev": "f707778d902af4d62d8dd92c269f8e70de09acbe",
"type": "github"
},
"original": {
"owner": "cachix",
"ref": "latest",
"repo": "cachix",
"type": "github"
}
},
"devenv": {
"inputs": {
"cachix": "cachix",
"flake-compat": "flake-compat",
"git-hooks": "git-hooks",
"nix": "nix",
"nixpkgs": [
"nixpkgs"
]
},
"locked": {
"lastModified": 1753888869,
"narHash": "sha256-VRYrrUmvXnBzfzuJVoI3os1H/0l8cJQ2KnrrxWkTB3E=",
"owner": "cachix",
"repo": "devenv",
"rev": "bdf26a4453eff6bae835f33d519a36f77e0ca257",
"type": "github"
},
"original": {
"owner": "cachix",
"repo": "devenv",
"type": "github"
}
},
"devenv-root": {
"flake": false,
"locked": {
"narHash": "sha256-d6xi4mKdjkX2JFicDIv5niSzpyI0m/Hnm8GGAIU04kY=",
"type": "file",
"url": "file:///dev/null"
},
"original": {
"type": "file",
"url": "file:///dev/null"
}
},
"flake-compat": {
"flake": false,
"locked": {
"lastModified": 1747046372,
"narHash": "sha256-CIVLLkVgvHYbgI2UpXvIIBJ12HWgX+fjA8Xf8PUmqCY=",
"owner": "edolstra",
"repo": "flake-compat",
"rev": "9100a0f413b0c601e0533d1d94ffd501ce2e7885",
"type": "github"
},
"original": {
"owner": "edolstra",
"repo": "flake-compat",
"type": "github"
}
},
"flake-parts": {
"inputs": {
"nixpkgs-lib": [
"devenv",
"nix",
"nixpkgs"
]
},
"locked": {
"lastModified": 1733312601,
"narHash": "sha256-4pDvzqnegAfRkPwO3wmwBhVi/Sye1mzps0zHWYnP88c=",
"owner": "hercules-ci",
"repo": "flake-parts",
"rev": "205b12d8b7cd4802fbcb8e8ef6a0f1408781a4f9",
"type": "github"
},
"original": {
"owner": "hercules-ci",
"repo": "flake-parts",
"type": "github"
}
},
"flake-parts_2": {
"inputs": {
"nixpkgs-lib": "nixpkgs-lib"
},
"locked": {
"lastModified": 1753121425,
"narHash": "sha256-TVcTNvOeWWk1DXljFxVRp+E0tzG1LhrVjOGGoMHuXio=",
"owner": "hercules-ci",
"repo": "flake-parts",
"rev": "644e0fc48951a860279da645ba77fe4a6e814c5e",
"type": "github"
},
"original": {
"owner": "hercules-ci",
"repo": "flake-parts",
"type": "github"
}
},
"git-hooks": {
"inputs": {
"flake-compat": [
"devenv",
"flake-compat"
],
"gitignore": "gitignore",
"nixpkgs": [
"devenv",
"nixpkgs"
]
},
"locked": {
"lastModified": 1750779888,
"narHash": "sha256-wibppH3g/E2lxU43ZQHC5yA/7kIKLGxVEnsnVK1BtRg=",
"owner": "cachix",
"repo": "git-hooks.nix",
"rev": "16ec914f6fb6f599ce988427d9d94efddf25fe6d",
"type": "github"
},
"original": {
"owner": "cachix",
"repo": "git-hooks.nix",
"type": "github"
}
},
"gitignore": {
"inputs": {
"nixpkgs": [
"devenv",
"git-hooks",
"nixpkgs"
]
},
"locked": {
"lastModified": 1709087332,
"narHash": "sha256-HG2cCnktfHsKV0s4XW83gU3F57gaTljL9KNSuG6bnQs=",
"owner": "hercules-ci",
"repo": "gitignore.nix",
"rev": "637db329424fd7e46cf4185293b9cc8c88c95394",
"type": "github"
},
"original": {
"owner": "hercules-ci",
"repo": "gitignore.nix",
"type": "github"
}
},
"nix": {
"inputs": {
"flake-compat": [
"devenv",
"flake-compat"
],
"flake-parts": "flake-parts",
"git-hooks-nix": [
"devenv",
"git-hooks"
],
"nixpkgs": [
"devenv",
"nixpkgs"
],
"nixpkgs-23-11": [
"devenv"
],
"nixpkgs-regression": [
"devenv"
]
},
"locked": {
"lastModified": 1752773918,
"narHash": "sha256-dOi/M6yNeuJlj88exI+7k154z+hAhFcuB8tZktiW7rg=",
"owner": "cachix",
"repo": "nix",
"rev": "031c3cf42d2e9391eee373507d8c12e0f9606779",
"type": "github"
},
"original": {
"owner": "cachix",
"ref": "devenv-2.30",
"repo": "nix",
"type": "github"
}
},
"nixpkgs": {
"locked": {
"lastModified": 1752827260,
"narHash": "sha256-noFjJbm/uWRcd2Lotr7ovedfhKVZT+LeJs9rU416lKQ=",
"owner": "nixos",
"repo": "nixpkgs",
"rev": "b527e89270879aaaf584c41f26b2796be634bc9d",
"type": "github"
},
"original": {
"owner": "nixos",
"repo": "nixpkgs",
"rev": "b527e89270879aaaf584c41f26b2796be634bc9d",
"type": "github"
}
},
"nixpkgs-lib": {
"locked": {
"lastModified": 1751159883,
"narHash": "sha256-urW/Ylk9FIfvXfliA1ywh75yszAbiTEVgpPeinFyVZo=",
"owner": "nix-community",
"repo": "nixpkgs.lib",
"rev": "14a40a1d7fb9afa4739275ac642ed7301a9ba1ab",
"type": "github"
},
"original": {
"owner": "nix-community",
"repo": "nixpkgs.lib",
"type": "github"
}
},
"root": {
"inputs": {
"devenv": "devenv",
"devenv-root": "devenv-root",
"flake-parts": "flake-parts_2",
"nixpkgs": "nixpkgs"
}
}
},
"root": "root",
"version": 7
}
+128
View File
@@ -0,0 +1,128 @@
{
inputs = {
# required for some reason when entering the shell for devenv
devenv-root = {
url = "file+file:///dev/null";
flake = false;
};
# node 24.4.1, postgres 17
nixpkgs.url = "github:nixos/nixpkgs/b527e89270879aaaf584c41f26b2796be634bc9d";
flake-parts.url = "github:hercules-ci/flake-parts";
devenv.url = "github:cachix/devenv";
devenv.inputs.nixpkgs.follows = "nixpkgs";
};
nixConfig = {
extra-trusted-public-keys = "devenv.cachix.org-1:w1cLUi8dv3hnoSPGAuibQv+f9TZLr6cv/Hm9XgU50cw=";
extra-substituters = "https://devenv.cachix.org";
};
outputs =
inputs@{ flake-parts, devenv-root, ... }:
flake-parts.lib.mkFlake { inherit inputs; } {
imports = [
inputs.devenv.flakeModule
];
systems = [
"x86_64-linux"
"x86_64-darwin"
"aarch64-linux"
"aarch64-darwin"
];
perSystem =
{
config,
self',
inputs',
pkgs,
system,
...
}:
let
psqlConfig = {
username = "postgres";
password = "postgres";
database = "zipline";
};
in
{
devenv.shells.default = {
packages = with pkgs; [
git
# to generate thumbnails
ffmpeg
# for testing docker
colima
docker
docker-compose
];
scripts = {
pgup.exec = ''
process-compose up postgres -D
'';
minioup.exec = ''
process-compose up minio -D
'';
downall.exec = ''
process-compose down
'';
# ensure that volumes are mounted with write access for docker containers
start_colima.exec = ''
colima start --mount $PWD/themes:w --mount $PWD/uploads:w --mount $PWD/public:w
'';
};
enterShell = ''
export name="zipline-env";
echo -e "\n[$name]: run 'pgup' to start services, 'pgdown' to stop services";
'';
languages.javascript = {
enable = true;
package = pkgs.nodejs_24;
corepack.enable = true;
};
services = {
postgres = {
enable = true;
package = pkgs.postgresql_17;
initialScript = ''
CREATE ROLE "${psqlConfig.username}" WITH LOGIN PASSWORD '${psqlConfig.password}' SUPERUSER;
'';
initialDatabases = [
{
name = psqlConfig.database;
user = psqlConfig.username;
}
];
listen_addresses = "0.0.0.0";
port = 5432;
};
minio = {
enable = true;
};
};
process.managers.process-compose = {
tui.enable = false;
};
};
};
};
}
+1385
View File
File diff suppressed because it is too large Load Diff
-5
View File
@@ -1,5 +0,0 @@
/// <reference types="next" />
/// <reference types="next/image-types/global" />
// NOTE: This file should not be edited
// see https://nextjs.org/docs/basic-features/typescript for more information.
-11
View File
@@ -1,11 +0,0 @@
module.exports = {
async redirects() {
return [
{
source: '/',
destination: '/dashboard',
permanent: true,
},
];
},
};
+121 -54
View File
@@ -1,63 +1,130 @@
{
"name": "zip3",
"version": "3.4.0",
"name": "zipline",
"private": true,
"license": "MIT",
"version": "4.4.2",
"scripts": {
"dev": "NODE_ENV=development node server",
"build": "npm-run-all build:schema build:next",
"build:next": "next build",
"build:schema": "prisma generate --schema=prisma/schema.prisma",
"migrate:dev": "prisma migrate dev --create-only",
"start": "node server",
"lint": "next lint",
"seed": "ts-node --compiler-options \"{\\\"module\\\":\\\"commonjs\\\"}\" --transpile-only prisma/seed.ts",
"docker:run": "docker-compose up -d",
"docker:down": "docker-compose down",
"docker:build-dev": "docker-compose --file docker-compose.dev.yml up --build"
"build": "tsx scripts/build.ts",
"dev": "cross-env NODE_ENV=development DEBUG=zipline tsx --require dotenv/config --enable-source-maps ./src/server",
"dev:nd": "cross-env NODE_ENV=development tsx --require dotenv/config --enable-source-maps ./src/server",
"dev:inspector": "cross-env NODE_ENV=development DEBUG=zipline tsx --require dotenv/config --inspect=0.0.0.0:9229 --enable-source-maps ./src/server",
"start": "cross-env NODE_ENV=production node --trace-warnings --require dotenv/config ./build/server",
"start:inspector": "cross-env NODE_ENV=production node --require dotenv/config --inspect=0.0.0.0:9229 --enable-source-maps ./build/server",
"ctl": "NODE_ENV=production node --require dotenv/config --enable-source-maps ./build/ctl",
"validate": "tsx scripts/validate.ts",
"openapi": "tsx scripts/openapi.ts",
"db:prototype": "prisma db push --skip-generate && prisma generate --no-hints",
"db:migrate": "prisma migrate dev --create-only",
"docker:engine": "colima start --mount $PWD/themes:w --mount $PWD/uploads:w --mount $PWD/public:w",
"docker:compose:dev:build": "docker compose --file docker-compose.dev.yml build --build-arg ZIPLINE_GIT_SHA=$(git rev-parse HEAD)",
"docker:compose:dev:up": "docker compose --file docker-compose.dev.yml up -d",
"docker:compose:dev:down": "docker compose --file docker-compose.dev.yml down",
"docker:compose:dev:logs": "docker compose --file docker-compose.dev.yml logs -f"
},
"dependencies": {
"@iarna/toml": "2.2.5",
"@mantine/core": "^3.6.9",
"@mantine/dropzone": "^3.6.9",
"@mantine/hooks": "^3.6.9",
"@mantine/modals": "^3.6.9",
"@mantine/next": "^3.6.9",
"@mantine/notifications": "^3.6.9",
"@mantine/prism": "^3.6.11",
"@modulz/radix-icons": "^4.0.0",
"@prisma/client": "^3.9.2",
"@prisma/migrate": "^3.9.2",
"@prisma/sdk": "^3.9.2",
"@reduxjs/toolkit": "^1.6.0",
"argon2": "^0.28.2",
"colorette": "^1.2.2",
"cookie": "^0.4.1",
"fecha": "^4.2.1",
"multer": "^1.4.2",
"next": "^12.1.0",
"prisma": "^3.9.2",
"react": "^17.0.2",
"react-dom": "^17.0.2",
"react-redux": "^7.2.4",
"react-table": "^7.7.0",
"redux": "^4.1.0",
"redux-thunk": "^2.3.0",
"uuid": "^8.3.2",
"yup": "^0.32.9"
"@aws-sdk/client-s3": "3.726.1",
"@aws-sdk/lib-storage": "3.726.1",
"@dnd-kit/core": "^6.3.1",
"@dnd-kit/sortable": "^10.0.0",
"@dnd-kit/utilities": "^3.2.2",
"@fastify/cookie": "^11.0.2",
"@fastify/cors": "^11.1.0",
"@fastify/multipart": "^9.3.0",
"@fastify/rate-limit": "^10.3.0",
"@fastify/sensible": "^6.0.4",
"@fastify/static": "^8.3.0",
"@fastify/swagger": "^9.6.1",
"@mantine/charts": "^8.3.9",
"@mantine/code-highlight": "^8.3.9",
"@mantine/core": "^8.3.9",
"@mantine/dates": "^8.3.9",
"@mantine/dropzone": "^8.3.9",
"@mantine/form": "^8.3.9",
"@mantine/hooks": "^8.3.9",
"@mantine/modals": "^8.3.9",
"@mantine/notifications": "^8.3.9",
"@prisma/adapter-pg": "6.13.0",
"@prisma/client": "6.13.0",
"@prisma/engines": "6.13.0",
"@prisma/internals": "6.13.0",
"@prisma/migrate": "6.13.0",
"@simplewebauthn/browser": "^13.2.2",
"@simplewebauthn/server": "^13.2.2",
"@smithy/node-http-handler": "^4.1.1",
"@tabler/icons-react": "^3.35.0",
"archiver": "^7.0.1",
"argon2": "^0.44.0",
"asciinema-player": "^3.12.1",
"bytes": "^3.1.2",
"clsx": "^2.1.1",
"colorette": "^2.0.20",
"commander": "^14.0.2",
"cookie": "^1.1.1",
"cross-env": "^10.1.0",
"dayjs": "^1.11.19",
"detect-browser": "^5.3.0",
"dotenv": "^17.2.3",
"fast-glob": "^3.3.3",
"fastify": "^5.6.2",
"fastify-plugin": "^5.1.0",
"fastify-type-provider-zod": "^6.1.0",
"fluent-ffmpeg": "^2.1.3",
"highlight.js": "^11.11.1",
"iron-session": "^8.0.4",
"isomorphic-dompurify": "^2.33.0",
"katex": "^0.16.27",
"mantine-datatable": "^8.3.9",
"ms": "^2.1.3",
"multer": "2.0.2",
"otplib": "^12.0.1",
"prisma": "6.13.0",
"qrcode": "^1.5.4",
"react": "^19.2.1",
"react-dom": "^19.2.1",
"react-markdown": "^10.1.0",
"react-router-dom": "^7.10.1",
"react-window": "1.8.11",
"remark-gfm": "^4.0.1",
"sharp": "^0.34.5",
"swr": "^2.3.7",
"typescript-eslint": "^8.48.1",
"vite": "^7.2.7",
"zod": "^4.1.13",
"zustand": "^5.0.9"
},
"devDependencies": {
"@types/cookie": "^0.4.0",
"@types/multer": "^1.4.6",
"@types/node": "^15.12.2",
"babel-plugin-import": "^1.13.3",
"eslint": "^7.32.0",
"eslint-config-next": "11.0.0",
"npm-run-all": "^4.1.5",
"ts-node": "^10.0.0",
"typescript": "^4.3.2"
"@types/archiver": "^7.0.0",
"@types/bytes": "^3.1.5",
"@types/fluent-ffmpeg": "^2.1.28",
"@types/katex": "^0.16.7",
"@types/ms": "^2.1.0",
"@types/multer": "^2.0.0",
"@types/node": "^24.10.1",
"@types/qrcode": "^1.5.6",
"@types/react": "^19.2.7",
"@types/react-dom": "^19.2.3",
"@types/react-window": "^1.8.8",
"@vitejs/plugin-react": "^5.1.1",
"eslint": "^9.39.1",
"eslint-config-prettier": "^10.1.8",
"eslint-plugin-jsx-a11y": "^6.10.2",
"eslint-plugin-prettier": "^5.5.4",
"eslint-plugin-react": "^7.37.5",
"eslint-plugin-react-hooks": "^7.0.1",
"eslint-plugin-react-refresh": "^0.4.24",
"eslint-plugin-unused-imports": "^4.3.0",
"postcss": "^8.5.6",
"postcss-preset-mantine": "^1.18.0",
"postcss-simple-vars": "^7.0.1",
"prettier": "^3.7.4",
"sass": "^1.94.2",
"tsc-alias": "^1.8.16",
"tsup": "^8.5.1",
"tsx": "^4.21.0",
"typescript": "^5.9.3"
},
"repository": {
"type": "git",
"url": "https://github.com/diced/zipline.git"
}
"engines": {
"node": ">=22"
},
"packageManager": "pnpm@10.30.1+sha512.3590e550d5384caa39bd5c7c739f72270234b2f6059e13018f975c313b1eb9fefcc09714048765d4d9efe961382c312e624572c0420762bdc5d5940cdf9be73a"
}
+11198
View File
File diff suppressed because it is too large Load Diff
+10
View File
@@ -0,0 +1,10 @@
ignoredBuiltDependencies:
- unrs-resolver
onlyBuiltDependencies:
- '@parcel/watcher'
- '@prisma/client'
- '@prisma/engines'
- argon2
- esbuild
- prisma
- sharp
+14
View File
@@ -0,0 +1,14 @@
module.exports = {
plugins: {
'postcss-preset-mantine': {},
'postcss-simple-vars': {
variables: {
'mantine-breakpoint-xs': '36em',
'mantine-breakpoint-sm': '48em',
'mantine-breakpoint-md': '62em',
'mantine-breakpoint-lg': '75em',
'mantine-breakpoint-xl': '88em',
},
},
},
};
+6
View File
@@ -0,0 +1,6 @@
/** @type {import('prettier').Config} */
module.exports = {
singleQuote: true,
jsxSingleQuote: true,
printWidth: 110,
};
@@ -1,71 +0,0 @@
-- CreateTable
CREATE TABLE "User" (
"id" SERIAL NOT NULL,
"username" TEXT NOT NULL,
"password" TEXT NOT NULL,
"token" TEXT NOT NULL,
"administrator" BOOLEAN NOT NULL DEFAULT false,
"embedTitle" TEXT,
"embedColor" TEXT NOT NULL DEFAULT E'#2f3136',
PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "Image" (
"id" SERIAL NOT NULL,
"file" TEXT NOT NULL,
"mimetype" TEXT NOT NULL DEFAULT E'image/png',
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"views" INTEGER NOT NULL DEFAULT 0,
"userId" INTEGER NOT NULL,
PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "InvisibleImage" (
"id" INTEGER NOT NULL,
"invis" TEXT NOT NULL
);
-- CreateTable
CREATE TABLE "Url" (
"id" SERIAL NOT NULL,
"to" TEXT NOT NULL,
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"views" INTEGER NOT NULL DEFAULT 0,
"userId" INTEGER NOT NULL,
PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "InvisibleUrl" (
"id" INTEGER NOT NULL,
"invis" TEXT NOT NULL
);
-- CreateIndex
CREATE UNIQUE INDEX "InvisibleImage.invis_unique" ON "InvisibleImage"("invis");
-- CreateIndex
CREATE UNIQUE INDEX "InvisibleImage_id_unique" ON "InvisibleImage"("id");
-- CreateIndex
CREATE UNIQUE INDEX "InvisibleUrl.invis_unique" ON "InvisibleUrl"("invis");
-- CreateIndex
CREATE UNIQUE INDEX "InvisibleUrl_id_unique" ON "InvisibleUrl"("id");
-- AddForeignKey
ALTER TABLE "Image" ADD FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "InvisibleImage" ADD FOREIGN KEY ("id") REFERENCES "Image"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "Url" ADD FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "InvisibleUrl" ADD FOREIGN KEY ("id") REFERENCES "Url"("id") ON DELETE CASCADE ON UPDATE CASCADE;
@@ -1,25 +0,0 @@
-- AlterTable
ALTER TABLE "User" ADD COLUMN "systemTheme" TEXT NOT NULL DEFAULT E'dark_blue';
-- CreateTable
CREATE TABLE "Theme" (
"id" SERIAL NOT NULL,
"type" TEXT NOT NULL,
"primary" TEXT NOT NULL,
"secondary" TEXT NOT NULL,
"error" TEXT NOT NULL,
"warning" TEXT NOT NULL,
"info" TEXT NOT NULL,
"border" TEXT NOT NULL,
"mainBackground" TEXT NOT NULL,
"paperBackground" TEXT NOT NULL,
"userId" INTEGER NOT NULL,
PRIMARY KEY ("id")
);
-- CreateIndex
CREATE UNIQUE INDEX "Theme_userId_unique" ON "Theme"("userId");
-- AddForeignKey
ALTER TABLE "Theme" ADD FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE;
@@ -1,2 +0,0 @@
-- AlterTable
ALTER TABLE "Image" ADD COLUMN "favorite" BOOLEAN NOT NULL DEFAULT false;
@@ -1,25 +0,0 @@
/*
Warnings:
- A unique constraint covering the columns `[imageId]` on the table `InvisibleImage` will be added. If there are existing duplicate values, this will fail.
- Added the required column `imageId` to the `InvisibleImage` table without a default value. This is not possible if the table is not empty.
*/
-- DropForeignKey
ALTER TABLE "InvisibleImage" DROP CONSTRAINT "InvisibleImage_id_fkey";
-- DropIndex
DROP INDEX "InvisibleImage_id_unique";
-- AlterTable
CREATE SEQUENCE "invisibleimage_id_seq";
ALTER TABLE "InvisibleImage" ADD COLUMN "imageId" INTEGER NOT NULL,
ALTER COLUMN "id" SET DEFAULT nextval('invisibleimage_id_seq'),
ADD PRIMARY KEY ("id");
ALTER SEQUENCE "invisibleimage_id_seq" OWNED BY "InvisibleImage"."id";
-- CreateIndex
CREATE UNIQUE INDEX "InvisibleImage_imageId_unique" ON "InvisibleImage"("imageId");
-- AddForeignKey
ALTER TABLE "InvisibleImage" ADD FOREIGN KEY ("imageId") REFERENCES "Image"("id") ON DELETE CASCADE ON UPDATE CASCADE;
@@ -1,2 +0,0 @@
-- AlterTable
ALTER TABLE "Image" ADD COLUMN "embed" BOOLEAN NOT NULL DEFAULT false;
@@ -1,39 +0,0 @@
/*
Warnings:
- You are about to drop the `InvisibleUrl` table. If the table is not empty, all the data it contains will be lost.
- You are about to drop the `Url` table. If the table is not empty, all the data it contains will be lost.
*/
-- DropForeignKey
ALTER TABLE "Image" DROP CONSTRAINT "Image_userId_fkey";
-- DropForeignKey
ALTER TABLE "InvisibleImage" DROP CONSTRAINT "InvisibleImage_imageId_fkey";
-- DropForeignKey
ALTER TABLE "InvisibleUrl" DROP CONSTRAINT "InvisibleUrl_id_fkey";
-- DropForeignKey
ALTER TABLE "Theme" DROP CONSTRAINT "Theme_userId_fkey";
-- DropForeignKey
ALTER TABLE "Url" DROP CONSTRAINT "Url_userId_fkey";
-- DropTable
DROP TABLE "InvisibleUrl";
-- DropTable
DROP TABLE "Url";
-- AddForeignKey
ALTER TABLE "Theme" ADD CONSTRAINT "Theme_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "Image" ADD CONSTRAINT "Image_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "InvisibleImage" ADD CONSTRAINT "InvisibleImage_imageId_fkey" FOREIGN KEY ("imageId") REFERENCES "Image"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
-- RenameIndex
ALTER INDEX "InvisibleImage.invis_unique" RENAME TO "InvisibleImage_invis_key";
@@ -1,34 +0,0 @@
-- CreateTable
CREATE TABLE "Url" (
"id" TEXT NOT NULL,
"destination" TEXT NOT NULL,
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"views" INTEGER NOT NULL DEFAULT 0,
"userId" INTEGER NOT NULL,
CONSTRAINT "Url_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "InvisibleUrl" (
"id" SERIAL NOT NULL,
"invis" TEXT NOT NULL,
"urlId" TEXT NOT NULL,
CONSTRAINT "InvisibleUrl_pkey" PRIMARY KEY ("id")
);
-- CreateIndex
CREATE UNIQUE INDEX "Url_id_key" ON "Url"("id");
-- CreateIndex
CREATE UNIQUE INDEX "InvisibleUrl_invis_key" ON "InvisibleUrl"("invis");
-- CreateIndex
CREATE UNIQUE INDEX "InvisibleUrl_urlId_unique" ON "InvisibleUrl"("urlId");
-- AddForeignKey
ALTER TABLE "Url" ADD CONSTRAINT "Url_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "InvisibleUrl" ADD CONSTRAINT "InvisibleUrl_urlId_fkey" FOREIGN KEY ("urlId") REFERENCES "Url"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
@@ -1,2 +0,0 @@
-- AlterTable
ALTER TABLE "Url" ADD COLUMN "vanity" TEXT;
@@ -1,2 +0,0 @@
-- AlterTable
ALTER TABLE "User" ADD COLUMN "embedSiteName" TEXT DEFAULT E'{image.file} • {user.name}';
@@ -1,11 +0,0 @@
-- AlterTable
ALTER TABLE "User" ADD COLUMN "ratelimited" BOOLEAN NOT NULL DEFAULT false;
-- RenameIndex
ALTER INDEX "InvisibleImage_imageId_unique" RENAME TO "InvisibleImage_imageId_key";
-- RenameIndex
ALTER INDEX "InvisibleUrl_urlId_unique" RENAME TO "InvisibleUrl_urlId_key";
-- RenameIndex
ALTER INDEX "Theme_userId_unique" RENAME TO "Theme_userId_key";
@@ -1,8 +0,0 @@
-- CreateTable
CREATE TABLE "Stats" (
"id" SERIAL NOT NULL,
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"data" JSONB NOT NULL,
CONSTRAINT "Stats_pkey" PRIMARY KEY ("id")
);
@@ -1,5 +0,0 @@
-- CreateEnum
CREATE TYPE "ImageFormat" AS ENUM ('UUID', 'DATE', 'RANDOM');
-- AlterTable
ALTER TABLE "Image" ADD COLUMN "format" "ImageFormat" NOT NULL DEFAULT E'RANDOM';
@@ -1,2 +0,0 @@
-- AlterEnum
ALTER TYPE "ImageFormat" ADD VALUE 'NAME';
@@ -1,12 +0,0 @@
/*
Warnings:
- You are about to drop the `Theme` table. If the table is not empty, all the data it contains will be lost.
*/
-- DropForeignKey
ALTER TABLE "Theme" DROP CONSTRAINT "Theme_userId_fkey";
-- AlterTable
ALTER TABLE "User" ALTER COLUMN "systemTheme" SET DEFAULT E'system';
-- DropTable
DROP TABLE "Theme";
@@ -0,0 +1,370 @@
-- CreateEnum
CREATE TYPE "UserFilesQuota" AS ENUM ('BY_BYTES', 'BY_FILES');
-- CreateEnum
CREATE TYPE "Role" AS ENUM ('USER', 'ADMIN', 'SUPERADMIN');
-- CreateEnum
CREATE TYPE "OAuthProviderType" AS ENUM ('DISCORD', 'GOOGLE', 'GITHUB', 'OIDC');
-- CreateEnum
CREATE TYPE "IncompleteFileStatus" AS ENUM ('PENDING', 'PROCESSING', 'COMPLETE', 'FAILED');
-- CreateTable
CREATE TABLE "Zipline" (
"id" TEXT NOT NULL,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,
"firstSetup" BOOLEAN NOT NULL DEFAULT true,
"coreReturnHttpsUrls" BOOLEAN NOT NULL DEFAULT false,
"coreDefaultDomain" TEXT,
"coreTempDirectory" TEXT NOT NULL,
"chunksEnabled" BOOLEAN NOT NULL DEFAULT true,
"chunksMax" INTEGER NOT NULL DEFAULT 99614720,
"chunksSize" INTEGER NOT NULL DEFAULT 26214400,
"tasksDeleteInterval" INTEGER NOT NULL DEFAULT 1800000,
"tasksClearInvitesInterval" INTEGER NOT NULL DEFAULT 1800000,
"tasksMaxViewsInterval" INTEGER NOT NULL DEFAULT 1800000,
"tasksThumbnailsInterval" INTEGER NOT NULL DEFAULT 1800000,
"tasksMetricsInterval" INTEGER NOT NULL DEFAULT 1800000,
"filesRoute" TEXT NOT NULL DEFAULT '/u',
"filesLength" INTEGER NOT NULL DEFAULT 6,
"filesDefaultFormat" TEXT NOT NULL DEFAULT 'random',
"filesDisabledExtensions" TEXT[],
"filesMaxFileSize" INTEGER NOT NULL DEFAULT 104857600,
"filesDefaultExpiration" INTEGER,
"filesAssumeMimetypes" BOOLEAN NOT NULL DEFAULT false,
"filesDefaultDateFormat" TEXT NOT NULL DEFAULT 'YYYY-MM-DD_HH:mm:ss',
"filesRemoveGpsMetadata" BOOLEAN NOT NULL DEFAULT false,
"urlsRoute" TEXT NOT NULL DEFAULT '/go',
"urlsLength" INTEGER NOT NULL DEFAULT 6,
"featuresImageCompression" BOOLEAN NOT NULL DEFAULT true,
"featuresRobotsTxt" BOOLEAN NOT NULL DEFAULT true,
"featuresHealthcheck" BOOLEAN NOT NULL DEFAULT true,
"featuresUserRegistration" BOOLEAN NOT NULL DEFAULT false,
"featuresOauthRegistration" BOOLEAN NOT NULL DEFAULT false,
"featuresDeleteOnMaxViews" BOOLEAN NOT NULL DEFAULT true,
"featuresThumbnailsEnabled" BOOLEAN NOT NULL DEFAULT true,
"featuresThumbnailsNumberThreads" INTEGER NOT NULL DEFAULT 4,
"featuresMetricsEnabled" BOOLEAN NOT NULL DEFAULT true,
"featuresMetricsAdminOnly" BOOLEAN NOT NULL DEFAULT false,
"featuresMetricsShowUserSpecific" BOOLEAN NOT NULL DEFAULT true,
"invitesEnabled" BOOLEAN NOT NULL DEFAULT true,
"invitesLength" INTEGER NOT NULL DEFAULT 6,
"websiteTitle" TEXT NOT NULL DEFAULT 'Zipline',
"websiteTitleLogo" TEXT,
"websiteExternalLinks" JSONB NOT NULL DEFAULT '[{ "name": "GitHub", "url": "https://github.com/diced/zipline"}, { "name": "Documentation", "url": "https://zipline.diced.sh/"}]',
"websiteLoginBackground" TEXT,
"websiteDefaultAvatar" TEXT,
"websiteTos" TEXT,
"websiteThemeDefault" TEXT NOT NULL DEFAULT 'system',
"websiteThemeDark" TEXT NOT NULL DEFAULT 'builtin:dark_gray',
"websiteThemeLight" TEXT NOT NULL DEFAULT 'builtin:light_gray',
"oauthBypassLocalLogin" BOOLEAN NOT NULL DEFAULT false,
"oauthLoginOnly" BOOLEAN NOT NULL DEFAULT false,
"oauthDiscordClientId" TEXT,
"oauthDiscordClientSecret" TEXT,
"oauthDiscordRedirectUri" TEXT,
"oauthGoogleClientId" TEXT,
"oauthGoogleClientSecret" TEXT,
"oauthGoogleRedirectUri" TEXT,
"oauthGithubClientId" TEXT,
"oauthGithubClientSecret" TEXT,
"oauthGithubRedirectUri" TEXT,
"oauthOidcClientId" TEXT,
"oauthOidcClientSecret" TEXT,
"oauthOidcAuthorizeUrl" TEXT,
"oauthOidcTokenUrl" TEXT,
"oauthOidcUserinfoUrl" TEXT,
"oauthOidcRedirectUri" TEXT,
"mfaTotpEnabled" BOOLEAN NOT NULL DEFAULT false,
"mfaTotpIssuer" TEXT NOT NULL DEFAULT 'Zipline',
"mfaPasskeys" BOOLEAN NOT NULL DEFAULT false,
"ratelimitEnabled" BOOLEAN NOT NULL DEFAULT true,
"ratelimitMax" INTEGER NOT NULL DEFAULT 10,
"ratelimitWindow" INTEGER,
"ratelimitAdminBypass" BOOLEAN NOT NULL DEFAULT true,
"ratelimitAllowList" TEXT[],
"httpWebhookOnUpload" TEXT,
"httpWebhookOnShorten" TEXT,
"discordWebhookUrl" TEXT,
"discordUsername" TEXT,
"discordAvatarUrl" TEXT,
"discordOnUploadWebhookUrl" TEXT,
"discordOnUploadUsername" TEXT,
"discordOnUploadAvatarUrl" TEXT,
"discordOnUploadContent" TEXT,
"discordOnUploadEmbed" JSONB,
"discordOnShortenWebhookUrl" TEXT,
"discordOnShortenUsername" TEXT,
"discordOnShortenAvatarUrl" TEXT,
"discordOnShortenContent" TEXT,
"discordOnShortenEmbed" JSONB,
"pwaEnabled" BOOLEAN NOT NULL DEFAULT false,
"pwaTitle" TEXT NOT NULL DEFAULT 'Zipline',
"pwaShortName" TEXT NOT NULL DEFAULT 'Zipline',
"pwaDescription" TEXT NOT NULL DEFAULT 'Zipline',
"pwaThemeColor" TEXT NOT NULL DEFAULT '#000000',
"pwaBackgroundColor" TEXT NOT NULL DEFAULT '#000000',
CONSTRAINT "Zipline_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "User" (
"id" TEXT NOT NULL,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,
"username" TEXT NOT NULL,
"password" TEXT,
"avatar" TEXT,
"token" TEXT NOT NULL,
"role" "Role" NOT NULL DEFAULT 'USER',
"view" JSONB NOT NULL DEFAULT '{}',
"totpSecret" TEXT,
"sessions" TEXT[],
CONSTRAINT "User_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "Export" (
"id" TEXT NOT NULL,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,
"completed" BOOLEAN NOT NULL DEFAULT false,
"path" TEXT NOT NULL,
"files" INTEGER NOT NULL,
"size" TEXT NOT NULL,
"userId" TEXT NOT NULL,
CONSTRAINT "Export_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "UserQuota" (
"id" TEXT NOT NULL,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,
"filesQuota" "UserFilesQuota" NOT NULL,
"maxBytes" TEXT,
"maxFiles" INTEGER,
"maxUrls" INTEGER,
"userId" TEXT,
CONSTRAINT "UserQuota_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "UserPasskey" (
"id" TEXT NOT NULL,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,
"lastUsed" TIMESTAMP(3),
"name" TEXT NOT NULL,
"reg" JSONB NOT NULL,
"userId" TEXT NOT NULL,
CONSTRAINT "UserPasskey_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "OAuthProvider" (
"id" TEXT NOT NULL,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,
"userId" TEXT NOT NULL,
"provider" "OAuthProviderType" NOT NULL,
"username" TEXT NOT NULL,
"accessToken" TEXT NOT NULL,
"refreshToken" TEXT,
"oauthId" TEXT,
CONSTRAINT "OAuthProvider_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "File" (
"id" TEXT NOT NULL,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,
"deletesAt" TIMESTAMP(3),
"name" TEXT NOT NULL,
"originalName" TEXT,
"size" BIGINT NOT NULL,
"type" TEXT NOT NULL,
"views" INTEGER NOT NULL DEFAULT 0,
"maxViews" INTEGER,
"favorite" BOOLEAN NOT NULL DEFAULT false,
"password" TEXT,
"userId" TEXT,
"folderId" TEXT,
CONSTRAINT "File_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "Thumbnail" (
"id" TEXT NOT NULL,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,
"path" TEXT NOT NULL,
"fileId" TEXT NOT NULL,
CONSTRAINT "Thumbnail_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "Folder" (
"id" TEXT NOT NULL,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,
"name" TEXT NOT NULL,
"public" BOOLEAN NOT NULL DEFAULT false,
"userId" TEXT NOT NULL,
CONSTRAINT "Folder_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "IncompleteFile" (
"id" TEXT NOT NULL,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,
"status" "IncompleteFileStatus" NOT NULL,
"chunksTotal" INTEGER NOT NULL,
"chunksComplete" INTEGER NOT NULL,
"metadata" JSONB NOT NULL,
"userId" TEXT NOT NULL,
CONSTRAINT "IncompleteFile_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "Tag" (
"id" TEXT NOT NULL,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,
"name" TEXT NOT NULL,
"color" TEXT NOT NULL,
"userId" TEXT,
CONSTRAINT "Tag_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "Url" (
"id" TEXT NOT NULL,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,
"code" TEXT NOT NULL,
"vanity" TEXT,
"destination" TEXT NOT NULL,
"views" INTEGER NOT NULL DEFAULT 0,
"maxViews" INTEGER,
"password" TEXT,
"userId" TEXT,
CONSTRAINT "Url_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "Metric" (
"id" TEXT NOT NULL,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,
"data" JSONB NOT NULL,
CONSTRAINT "Metric_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "Invite" (
"id" TEXT NOT NULL,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,
"expiresAt" TIMESTAMP(3),
"code" TEXT NOT NULL,
"uses" INTEGER NOT NULL DEFAULT 0,
"maxUses" INTEGER,
"inviterId" TEXT NOT NULL,
CONSTRAINT "Invite_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "_FileToTag" (
"A" TEXT NOT NULL,
"B" TEXT NOT NULL,
CONSTRAINT "_FileToTag_AB_pkey" PRIMARY KEY ("A","B")
);
-- CreateIndex
CREATE UNIQUE INDEX "User_username_key" ON "User"("username");
-- CreateIndex
CREATE UNIQUE INDEX "User_token_key" ON "User"("token");
-- CreateIndex
CREATE UNIQUE INDEX "UserQuota_userId_key" ON "UserQuota"("userId");
-- CreateIndex
CREATE UNIQUE INDEX "OAuthProvider_provider_oauthId_key" ON "OAuthProvider"("provider", "oauthId");
-- CreateIndex
CREATE UNIQUE INDEX "Thumbnail_fileId_key" ON "Thumbnail"("fileId");
-- CreateIndex
CREATE UNIQUE INDEX "Tag_name_key" ON "Tag"("name");
-- CreateIndex
CREATE UNIQUE INDEX "Url_code_vanity_key" ON "Url"("code", "vanity");
-- CreateIndex
CREATE UNIQUE INDEX "Invite_code_key" ON "Invite"("code");
-- CreateIndex
CREATE INDEX "_FileToTag_B_index" ON "_FileToTag"("B");
-- AddForeignKey
ALTER TABLE "Export" ADD CONSTRAINT "Export_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "UserQuota" ADD CONSTRAINT "UserQuota_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "UserPasskey" ADD CONSTRAINT "UserPasskey_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "OAuthProvider" ADD CONSTRAINT "OAuthProvider_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "File" ADD CONSTRAINT "File_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE SET NULL ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "File" ADD CONSTRAINT "File_folderId_fkey" FOREIGN KEY ("folderId") REFERENCES "Folder"("id") ON DELETE SET NULL ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "Thumbnail" ADD CONSTRAINT "Thumbnail_fileId_fkey" FOREIGN KEY ("fileId") REFERENCES "File"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "Folder" ADD CONSTRAINT "Folder_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "IncompleteFile" ADD CONSTRAINT "IncompleteFile_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "Tag" ADD CONSTRAINT "Tag_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE SET NULL ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "Url" ADD CONSTRAINT "Url_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE SET NULL ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "Invite" ADD CONSTRAINT "Invite_inviterId_fkey" FOREIGN KEY ("inviterId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "_FileToTag" ADD CONSTRAINT "_FileToTag_A_fkey" FOREIGN KEY ("A") REFERENCES "File"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "_FileToTag" ADD CONSTRAINT "_FileToTag_B_fkey" FOREIGN KEY ("B") REFERENCES "Tag"("id") ON DELETE CASCADE ON UPDATE CASCADE;
@@ -0,0 +1,2 @@
-- AlterTable
ALTER TABLE "Zipline" ALTER COLUMN "filesDefaultExpiration" SET DATA TYPE TEXT;
@@ -0,0 +1,17 @@
-- AlterTable
ALTER TABLE "Zipline" ALTER COLUMN "chunksMax" SET DEFAULT '95mb',
ALTER COLUMN "chunksMax" SET DATA TYPE TEXT,
ALTER COLUMN "chunksSize" SET DEFAULT '25mb',
ALTER COLUMN "chunksSize" SET DATA TYPE TEXT,
ALTER COLUMN "tasksDeleteInterval" SET DEFAULT '30m',
ALTER COLUMN "tasksDeleteInterval" SET DATA TYPE TEXT,
ALTER COLUMN "tasksClearInvitesInterval" SET DEFAULT '30m',
ALTER COLUMN "tasksClearInvitesInterval" SET DATA TYPE TEXT,
ALTER COLUMN "tasksMaxViewsInterval" SET DEFAULT '30m',
ALTER COLUMN "tasksMaxViewsInterval" SET DATA TYPE TEXT,
ALTER COLUMN "tasksThumbnailsInterval" SET DEFAULT '30m',
ALTER COLUMN "tasksThumbnailsInterval" SET DATA TYPE TEXT,
ALTER COLUMN "tasksMetricsInterval" SET DEFAULT '30m',
ALTER COLUMN "tasksMetricsInterval" SET DATA TYPE TEXT,
ALTER COLUMN "filesMaxFileSize" SET DEFAULT '100mb',
ALTER COLUMN "filesMaxFileSize" SET DATA TYPE TEXT;
@@ -0,0 +1,2 @@
-- AlterTable
ALTER TABLE "Zipline" ADD COLUMN "websiteLoginBackgroundBlur" BOOLEAN NOT NULL DEFAULT true;
@@ -0,0 +1,2 @@
-- AlterTable
ALTER TABLE "Url" ADD COLUMN "enabled" BOOLEAN NOT NULL DEFAULT true;
@@ -0,0 +1,3 @@
-- AlterTable
ALTER TABLE "Zipline" ADD COLUMN "filesRandomWordsNumAdjectives" INTEGER NOT NULL DEFAULT 2,
ADD COLUMN "filesRandomWordsSeparator" TEXT NOT NULL DEFAULT '-';
@@ -0,0 +1,2 @@
-- AlterTable
ALTER TABLE "Folder" ADD COLUMN "allowUploads" BOOLEAN NOT NULL DEFAULT false;
@@ -0,0 +1,3 @@
-- AlterTable
ALTER TABLE "Zipline" ADD COLUMN "featuresVersionAPI" TEXT NOT NULL DEFAULT 'https://zipline-version.diced.sh',
ADD COLUMN "featuresVersionChecking" BOOLEAN NOT NULL DEFAULT true;
@@ -0,0 +1,2 @@
-- AlterTable
ALTER TABLE "Zipline" ADD COLUMN "oauthDiscordWhitelistIds" TEXT[] DEFAULT ARRAY[]::TEXT[];
@@ -0,0 +1,10 @@
/*
Warnings:
- You are about to drop the column `oauthDiscordWhitelistIds` on the `Zipline` table. All the data in the column will be lost.
*/
-- AlterTable
ALTER TABLE "Zipline" DROP COLUMN "oauthDiscordWhitelistIds",
ADD COLUMN "oauthDiscordAllowedIds" TEXT[] DEFAULT ARRAY[]::TEXT[],
ADD COLUMN "oauthDiscordDeniedIds" TEXT[] DEFAULT ARRAY[]::TEXT[];
@@ -0,0 +1,2 @@
-- AlterTable
ALTER TABLE "Zipline" ADD COLUMN "domains" TEXT[] DEFAULT ARRAY[]::TEXT[];
@@ -0,0 +1,2 @@
-- AlterTable
ALTER TABLE "public"."Zipline" ADD COLUMN "filesDefaultCompressionFormat" TEXT DEFAULT 'jpg';
@@ -0,0 +1,2 @@
-- AlterTable
ALTER TABLE "public"."Zipline" ADD COLUMN "featuresThumbnailsFormat" TEXT NOT NULL DEFAULT 'jpg';
@@ -0,0 +1,2 @@
-- AlterTable
ALTER TABLE "public"."Zipline" ADD COLUMN "coreTrustProxy" BOOLEAN NOT NULL DEFAULT false;
@@ -0,0 +1,2 @@
-- AlterTable
ALTER TABLE "public"."Zipline" ADD COLUMN "filesMaxExpiration" TEXT;
@@ -0,0 +1,11 @@
/*
Warnings:
- You are about to drop the column `mfaPasskeys` on the `Zipline` table. All the data in the column will be lost.
*/
-- AlterTable
ALTER TABLE "public"."Zipline" DROP COLUMN "mfaPasskeys",
ADD COLUMN "mfaPasskeysEnabled" BOOLEAN NOT NULL DEFAULT false,
ADD COLUMN "mfaPasskeysOrigin" TEXT,
ADD COLUMN "mfaPasskeysRpID" TEXT;
@@ -0,0 +1,2 @@
-- AlterTable
ALTER TABLE "public"."Zipline" ADD COLUMN "tasksCleanThumbnailsInterval" TEXT NOT NULL DEFAULT '1d';
@@ -0,0 +1,6 @@
-- AlterTable
ALTER TABLE "public"."Folder" ADD COLUMN "parentId" TEXT;
-- AddForeignKey
ALTER TABLE "public"."Folder" ADD CONSTRAINT "Folder_parentId_fkey" FOREIGN KEY ("parentId") REFERENCES "public"."Folder"("id") ON DELETE SET NULL ON UPDATE CASCADE;
@@ -0,0 +1,23 @@
/*
Warnings:
- You are about to drop the column `sessions` on the `User` table. All the data in the column will be lost.
*/
-- AlterTable
ALTER TABLE "public"."User" DROP COLUMN "sessions";
-- CreateTable
CREATE TABLE "public"."UserSession" (
"id" TEXT NOT NULL,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"ua" TEXT NOT NULL,
"client" TEXT NOT NULL,
"device" TEXT NOT NULL,
"userId" TEXT NOT NULL,
CONSTRAINT "UserSession_pkey" PRIMARY KEY ("id")
);
-- AddForeignKey
ALTER TABLE "public"."UserSession" ADD CONSTRAINT "UserSession_userId_fkey" FOREIGN KEY ("userId") REFERENCES "public"."User"("id") ON DELETE CASCADE ON UPDATE CASCADE;
+2 -2
View File
@@ -1,3 +1,3 @@
# Please do not edit this file manually
# It should be added in your version-control system (i.e. Git)
provider = "postgresql"
# It should be added in your version-control system (e.g., Git)
provider = "postgresql"
+381 -53
View File
@@ -1,75 +1,403 @@
generator client {
provider = "prisma-client"
output = "../src/prisma"
moduleFormat = "cjs"
previewFeatures = ["queryCompiler", "driverAdapters"]
}
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}
generator client {
provider = "prisma-client-js"
model Zipline {
id String @id @default(cuid())
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
firstSetup Boolean @default(true)
coreReturnHttpsUrls Boolean @default(false)
coreDefaultDomain String?
coreTempDirectory String // default join(tmpdir(), 'zipline')
coreTrustProxy Boolean @default(false)
chunksEnabled Boolean @default(true)
chunksMax String @default("95mb")
chunksSize String @default("25mb")
tasksDeleteInterval String @default("30m")
tasksClearInvitesInterval String @default("30m")
tasksMaxViewsInterval String @default("30m")
tasksThumbnailsInterval String @default("30m")
tasksMetricsInterval String @default("30m")
tasksCleanThumbnailsInterval String @default("1d")
filesRoute String @default("/u")
filesLength Int @default(6)
filesDefaultFormat String @default("random")
filesDisabledExtensions String[]
filesMaxFileSize String @default("100mb")
filesDefaultExpiration String?
filesMaxExpiration String?
filesAssumeMimetypes Boolean @default(false)
filesDefaultDateFormat String @default("YYYY-MM-DD_HH:mm:ss")
filesRemoveGpsMetadata Boolean @default(false)
filesRandomWordsNumAdjectives Int @default(2)
filesRandomWordsSeparator String @default("-")
filesDefaultCompressionFormat String? @default("jpg")
urlsRoute String @default("/go")
urlsLength Int @default(6)
featuresImageCompression Boolean @default(true)
featuresRobotsTxt Boolean @default(true)
featuresHealthcheck Boolean @default(true)
featuresUserRegistration Boolean @default(false)
featuresOauthRegistration Boolean @default(false)
featuresDeleteOnMaxViews Boolean @default(true)
featuresThumbnailsEnabled Boolean @default(true)
featuresThumbnailsNumberThreads Int @default(4)
featuresThumbnailsFormat String @default("jpg")
featuresMetricsEnabled Boolean @default(true)
featuresMetricsAdminOnly Boolean @default(false)
featuresMetricsShowUserSpecific Boolean @default(true)
featuresVersionChecking Boolean @default(true)
featuresVersionAPI String @default("https://zipline-version.diced.sh")
invitesEnabled Boolean @default(true)
invitesLength Int @default(6)
websiteTitle String @default("Zipline")
websiteTitleLogo String?
websiteExternalLinks Json @default("[{ \"name\": \"GitHub\", \"url\": \"https://github.com/diced/zipline\"}, { \"name\": \"Documentation\", \"url\": \"https://zipline.diced.sh/\"}]")
websiteLoginBackground String?
websiteLoginBackgroundBlur Boolean @default(true)
websiteDefaultAvatar String?
websiteTos String?
websiteThemeDefault String @default("system")
websiteThemeDark String @default("builtin:dark_gray")
websiteThemeLight String @default("builtin:light_gray")
oauthBypassLocalLogin Boolean @default(false)
oauthLoginOnly Boolean @default(false)
oauthDiscordClientId String?
oauthDiscordClientSecret String?
oauthDiscordRedirectUri String?
oauthDiscordAllowedIds String[] @default([])
oauthDiscordDeniedIds String[] @default([])
oauthGoogleClientId String?
oauthGoogleClientSecret String?
oauthGoogleRedirectUri String?
oauthGithubClientId String?
oauthGithubClientSecret String?
oauthGithubRedirectUri String?
oauthOidcClientId String?
oauthOidcClientSecret String?
oauthOidcAuthorizeUrl String?
oauthOidcTokenUrl String?
oauthOidcUserinfoUrl String?
oauthOidcRedirectUri String?
mfaTotpEnabled Boolean @default(false)
mfaTotpIssuer String @default("Zipline")
mfaPasskeysEnabled Boolean @default(false)
mfaPasskeysRpID String?
mfaPasskeysOrigin String?
ratelimitEnabled Boolean @default(true)
ratelimitMax Int @default(10)
ratelimitWindow Int?
ratelimitAdminBypass Boolean @default(true)
ratelimitAllowList String[]
httpWebhookOnUpload String?
httpWebhookOnShorten String?
discordWebhookUrl String?
discordUsername String?
discordAvatarUrl String?
discordOnUploadWebhookUrl String?
discordOnUploadUsername String?
discordOnUploadAvatarUrl String?
discordOnUploadContent String?
discordOnUploadEmbed Json?
discordOnShortenWebhookUrl String?
discordOnShortenUsername String?
discordOnShortenAvatarUrl String?
discordOnShortenContent String?
discordOnShortenEmbed Json?
pwaEnabled Boolean @default(false)
pwaTitle String @default("Zipline")
pwaShortName String @default("Zipline")
pwaDescription String @default("Zipline")
pwaThemeColor String @default("#000000")
pwaBackgroundColor String @default("#000000")
domains String[] @default([])
}
model User {
id Int @id @default(autoincrement())
username String
password String
token String
administrator Boolean @default(false)
systemTheme String @default("system")
embedTitle String?
embedColor String @default("#2f3136")
embedSiteName String? @default("{image.file} • {user.name}")
ratelimited Boolean @default(false)
images Image[]
urls Url[]
id String @id @default(cuid())
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
username String @unique
password String?
avatar String?
token String @unique
role Role @default(USER)
view Json @default("{}")
totpSecret String?
passkeys UserPasskey[]
sessions UserSession[]
quota UserQuota?
files File[]
urls Url[]
folders Folder[]
invites Invite[]
tags Tag[]
oauthProviders OAuthProvider[]
IncompleteFile IncompleteFile[]
exports Export[]
}
enum ImageFormat {
UUID
DATE
RANDOM
NAME
model Export {
id String @id @default(cuid())
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
completed Boolean @default(false)
path String
files Int
size String
User User @relation(fields: [userId], references: [id], onDelete: Cascade, onUpdate: Cascade)
userId String
}
model Image {
id Int @id @default(autoincrement())
file String
mimetype String @default("image/png")
created_at DateTime @default(now())
views Int @default(0)
favorite Boolean @default(false)
embed Boolean @default(false)
invisible InvisibleImage?
format ImageFormat @default(RANDOM)
user User @relation(fields: [userId], references: [id])
userId Int
model UserSession {
id String @id
createdAt DateTime @default(now())
ua String
client String
device String
User User @relation(fields: [userId], references: [id], onDelete: Cascade, onUpdate: Cascade)
userId String
}
model InvisibleImage {
id Int @id @default(autoincrement())
invis String @unique
imageId Int
image Image @relation(fields: [imageId], references: [id])
model UserQuota {
id String @id @default(cuid())
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
filesQuota UserFilesQuota
maxBytes String?
maxFiles Int?
maxUrls Int?
User User? @relation(fields: [userId], references: [id], onDelete: Cascade)
userId String? @unique
}
enum UserFilesQuota {
BY_BYTES
BY_FILES
}
model UserPasskey {
id String @id @default(cuid())
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
lastUsed DateTime?
name String
reg Json
User User @relation(fields: [userId], references: [id], onDelete: Cascade, onUpdate: Cascade)
userId String
}
enum Role {
USER
ADMIN
SUPERADMIN
}
model OAuthProvider {
id String @id @default(cuid())
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
userId String
provider OAuthProviderType
username String
accessToken String
refreshToken String?
oauthId String?
user User @relation(fields: [userId], references: [id])
@@unique([provider, oauthId])
}
enum OAuthProviderType {
DISCORD
GOOGLE
GITHUB
OIDC
}
model File {
id String @id @default(cuid())
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
deletesAt DateTime?
name String // name & file saved on datasource
originalName String? // original name of file when uploaded
size BigInt
type String
views Int @default(0)
maxViews Int?
favorite Boolean @default(false)
password String?
tags Tag[]
User User? @relation(fields: [userId], references: [id], onDelete: SetNull, onUpdate: Cascade)
userId String?
Folder Folder? @relation(fields: [folderId], references: [id], onDelete: SetNull, onUpdate: Cascade)
folderId String?
thumbnail Thumbnail?
}
model Thumbnail {
id String @id @default(cuid())
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
path String
file File @relation(fields: [fileId], references: [id], onDelete: Cascade, onUpdate: Cascade)
fileId String
@@unique([fileId])
}
model Folder {
id String @id @default(cuid())
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
name String
public Boolean @default(false)
allowUploads Boolean @default(false)
files File[]
parentId String?
parent Folder? @relation("FolderToFolder", fields: [parentId], references: [id], onDelete: SetNull, onUpdate: Cascade)
children Folder[] @relation("FolderToFolder")
User User @relation(fields: [userId], references: [id], onDelete: Cascade, onUpdate: Cascade)
userId String
}
model IncompleteFile {
id String @id @default(cuid())
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
status IncompleteFileStatus
chunksTotal Int
chunksComplete Int
metadata Json
User User @relation(fields: [userId], references: [id], onDelete: Cascade, onUpdate: Cascade)
userId String
}
enum IncompleteFileStatus {
PENDING
PROCESSING
COMPLETE
FAILED
}
model Tag {
id String @id @default(cuid())
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
name String @unique
color String
files File[]
User User? @relation(fields: [userId], references: [id])
userId String?
}
model Url {
id String @id @unique
destination String
id String @id @default(cuid())
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
code String
vanity String?
created_at DateTime @default(now())
views Int @default(0)
invisible InvisibleUrl?
user User @relation(fields: [userId], references: [id])
userId Int
destination String
views Int @default(0)
maxViews Int?
password String?
enabled Boolean @default(true)
User User? @relation(fields: [userId], references: [id], onDelete: SetNull, onUpdate: Cascade)
userId String?
@@unique([code, vanity])
}
model InvisibleUrl {
id Int @id @default(autoincrement())
invis String @unique
urlId String
url Url @relation(fields: [urlId], references: [id])
model Metric {
id String @id @default(cuid())
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
data Json
}
model Stats {
id Int @id @default(autoincrement())
created_at DateTime @default(now())
data Json
}
model Invite {
id String @id @default(cuid())
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
expiresAt DateTime?
code String @unique
uses Int @default(0)
maxUses Int?
inviter User @relation(fields: [inviterId], references: [id], onDelete: Cascade, onUpdate: Cascade)
inviterId String
}
-31
View File
@@ -1,31 +0,0 @@
import { PrismaClient } from '@prisma/client';
import { hashPassword, createToken } from '../src/lib/util';
const prisma = new PrismaClient();
async function main() {
const user = await prisma.user.create({
data: {
username: 'administrator',
password: await hashPassword('password'),
token: createToken(),
administrator: true,
},
});
console.log(`
When logging into Zipline for the first time, use these credentials:
Username: "${user.username}"
Password: "password"
`);
}
main()
.catch(e => {
console.error(e);
process.exit(1);
})
.finally(async () => {
await prisma.$disconnect();
});
File diff suppressed because it is too large Load Diff
+1750
View File
File diff suppressed because it is too large Load Diff
Binary file not shown.

After

Width:  |  Height:  |  Size: 5.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 582 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 279 KiB

After

Width:  |  Height:  |  Size: 279 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.8 KiB

After

Width:  |  Height:  |  Size: 28 KiB

+24
View File
@@ -0,0 +1,24 @@
import { run, step } from '.';
import { lintStep } from './lint';
run(
'build',
lintStep,
step('prisma', 'prisma generate'),
step('typecheck', 'tsc', () => !process.argv.includes('--skip')),
// builds
step('server', 'tsup'),
// client stuff
step('client', 'vite build'),
step(
'client/ssr/view',
'vite build --ssr ssr-view/server.tsx -m ssr-view --outDir ../../build/ssr --emptyOutDir=false',
),
step(
'client/ssr/view-url',
'vite build --ssr ssr-view-url/server.tsx -m ssr-view-url --outDir ../../build/ssr --emptyOutDir=false',
),
);
-38
View File
@@ -1,38 +0,0 @@
// https://github.com/toptal/haste-server/blob/master/static/application.js#L167-L174
// Popular extension map
module.exports = {
rb: 'ruby',
py: 'python',
pl: 'perl',
php: 'php',
scala: 'scala',
go: 'go',
xml: 'xml',
html: 'xml',
htm: 'xml',
css: 'css',
js: 'javascript',
json: 'json',
vbs: 'vbscript',
lua: 'lua',
pas: 'delphi',
java: 'java',
cpp: 'cpp',
cc: 'cpp',
m: 'objectivec',
vala: 'vala',
sql: 'sql',
sm: 'smalltalk',
lisp: 'lisp',
ini: 'ini',
diff: 'diff',
bash: 'bash',
sh: 'bash',
tex: 'tex',
erl: 'erlang',
hs: 'haskell',
md: 'markdown',
txt: '',
coffee: 'coffee',
swift: 'swift',
};
+55
View File
@@ -0,0 +1,55 @@
type StepCommand = string | (() => void | Promise<void>);
export function step(name: string, command: StepCommand, condition: () => boolean = () => true) {
return {
name,
command,
condition,
};
}
export type Step = ReturnType<typeof step>;
function log(message: string) {
console.log(`\n${message}\n`);
}
export async function run(name: string, ...steps: Step[]) {
const { execSync } = await import('child_process');
const runOne = process.argv[2];
if (runOne) {
const match = steps.find((s) => `${name}/${s.name}` === runOne);
if (!match) {
console.error(`x No step found with name "${runOne}"`);
process.exit(1);
}
steps = [match];
}
const start = process.hrtime();
for (const step of steps) {
if (!step.condition()) {
log(`- Skipping step "${name}/${step.name}"...`);
continue;
}
try {
log(`> Running step "${name}/${step.name}"...`);
if (typeof step.command === 'string') {
execSync(step.command, { stdio: 'inherit' });
} else {
await step.command();
}
} catch {
console.error(`x Step "${name}/${step.name}" failed.`);
process.exit(1);
}
}
const diff = process.hrtime(start);
const time = diff[0] * 1e9 + diff[1];
const timeStr = time > 1e9 ? `${(time / 1e9).toFixed(2)}s` : `${(time / 1e6).toFixed(2)}ms`;
log(`✓ Steps in "${name}" completed in ${timeStr}.`);
}
+3
View File
@@ -0,0 +1,3 @@
import { step } from '.';
export const lintStep = step('lint', 'eslint .');
-35
View File
@@ -1,35 +0,0 @@
const { readdir } = require('fs/promises');
const { extname } = require('path');
const validateConfig = require('../server/validateConfig');
const Logger = require('../src/lib/logger');
const readConfig = require('../src/lib/readConfig');
const mimes = require('./mimes');
const { PrismaClient } = require('@prisma/client');
(async () => {
const config = readConfig();
await validateConfig(config);
process.env.DATABASE_URL = config.core.database_url;
const files = await readdir(process.argv[2]);
const data = files.map(x => {
const mime = mimes[extname(x)] ?? 'application/octet-stream';
return {
file: x,
mimetype: mime,
userId: 1,
};
});
const prisma = new PrismaClient();
Logger.get('migrator').info('starting migrations...');
await prisma.image.createMany({
data,
});
Logger.get('migrator').info('finished migrations! It is recomended to move your old uploads folder (' + process.argv[2] + ') to the current one which is ' + config.uploader.directory);
process.exit();
})();
-78
View File
@@ -1,78 +0,0 @@
module.exports = {
'.aac': 'audio/aac',
'.abw': 'application/x-abiword',
'.arc': 'application/x-freearc',
'.avi': 'video/x-msvideo',
'.azw': 'application/vnd.amazon.ebook',
'.bin': 'application/octet-stream',
'.bmp': 'image/bmp',
'.bz': 'application/x-bzip',
'.bz2': 'application/x-bzip2',
'.cda': 'application/x-cdf',
'.csh': 'application/x-csh',
'.css': 'text/css',
'.csv': 'text/csv',
'.doc': 'application/msword',
'.docx': 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
'.eot': 'application/vnd.ms-fontobject',
'.epub': 'application/epub+zip',
'.gz': 'application/gzip',
'.gif': 'image/gif',
'.htm': 'text/html',
'.html': 'text/html',
'.ico': 'image/vnd.microsoft.icon',
'.ics': 'text/calendar',
'.jar': 'application/java-archive',
'.jpeg': 'image/jpeg',
'.jpg': 'image/jpeg',
'.js': 'text/javascript',
'.json': 'application/json',
'.jsonld': 'application/ld+json',
'.mid': 'audio/midi',
'.midi': 'audio/midi',
'.mjs': 'text/javascript',
'.mp3': 'audio/mpeg',
'.mp4': 'video/mp4',
'.mpeg': 'video/mpeg',
'.mpkg': 'application/vnd.apple.installer+xml',
'.odp': 'application/vnd.oasis.opendocument.presentation',
'.ods': 'application/vnd.oasis.opendocument.spreadsheet',
'.odt': 'application/vnd.oasis.opendocument.text',
'.oga': 'audio/ogg',
'.ogv': 'video/ogg',
'.ogx': 'application/ogg',
'.opus': 'audio/opus',
'.otf': 'font/otf',
'.png': 'image/png',
'.pdf': 'application/pdf',
'.php': 'application/x-httpd-php',
'.ppt': 'application/vnd.ms-powerpoint',
'.pptx': 'application/vnd.openxmlformats-officedocument.presentationml.presentation',
'.rar': 'application/vnd.rar',
'.rtf': 'application/rtf',
'.sh': 'application/x-sh',
'.svg': 'image/svg+xml',
'.swf': 'application/x-shockwave-flash',
'.tar': 'application/x-tar',
'.tif': 'image/tiff',
'.tiff': 'image/tiff',
'.ts': 'video/mp2t',
'.ttf': 'font/ttf',
'.txt': 'text/plain',
'.vsd': 'application/vnd.visio',
'.wav': 'audio/wav',
'.weba': 'audio/webm',
'.webm': 'video/webm',
'.webp': 'image/webp',
'.woff': 'font/woff',
'.woff2': 'font/woff2',
'.xhtml': 'application/xhtml+xml',
'.xls': 'application/vnd.ms-excel',
'.xlsx': 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
'.xml': 'application/xml',
'.xul': 'application/vnd.mozilla.xul+xml',
'.zip': 'application/zip',
'.3gp': 'video/3gpp',
'.3g2': 'video/3gpp2',
'.7z': 'application/x-7z-compressed',
};
+110
View File
@@ -0,0 +1,110 @@
import { readFile, writeFile } from 'fs/promises';
import path from 'path';
import { run, step } from '.';
import { API_ERRORS, ApiError, ApiErrorCode } from '../src/lib/api/errors';
const ALL_METHODS = ['delete', 'get', 'head', 'patch', 'post', 'put'];
const GEN_PATH = path.resolve(__dirname, '..', 'openapi.json');
const ALL_ERRORS = Object.keys(API_ERRORS)
.map((code) => new ApiError(Number(code) as ApiErrorCode).toJSON())
.sort((a, b) => a.code - b.code);
const ERROR_SCHEMA = {
type: 'object',
description: 'Generic error for API endpoints.',
properties: {
error: {
type: 'string',
description:
'Message for the error. This may differ from the standard message for the error code, but the error code should be used to figure out the type of error.',
},
code: {
type: 'integer',
format: 'int32',
description:
'Zipline API error code. Ranges: 1xxx validation, 2xxx session, 3xxx permission, 4xxx not-found, 5xxx constraint, 6xxx internal, 9xxx generic.',
enum: ALL_ERRORS.map((entry) => entry.code),
'x-enumDescriptions': ALL_ERRORS.map((entry) => entry.message),
},
statusCode: {
type: 'integer',
format: 'int32',
description: 'HTTP status code returned alongside this error payload.',
},
},
required: ['error', 'code', 'statusCode'],
additionalProperties: true,
};
const ERROR_EXAMPLES = ALL_ERRORS.reduce<Record<string, unknown>>((examples, entry) => {
examples[`E${entry.code}`] = {
summary: `${entry.error}`,
value: entry,
};
return examples;
}, {});
const generic4xxResponse = {
description: 'API error response (4xx)',
content: {
'application/json': {
schema: ERROR_SCHEMA,
examples: ERROR_EXAMPLES,
},
},
};
function addErrorResponse(responses: Record<string, any>): void {
const response = (responses['4xx'] ??= structuredClone(generic4xxResponse));
response.description ??= generic4xxResponse.description;
response.content ??= {};
const jsonContent = (response.content['application/json'] ??= {});
jsonContent.schema ??= structuredClone(ERROR_SCHEMA);
jsonContent.examples ??= structuredClone(generic4xxResponse.content['application/json'].examples);
}
function filterRoutes(paths = {}): Record<string, any> {
return Object.fromEntries(Object.entries(paths).filter(([route]) => route.startsWith('/api')));
}
async function fixSpec() {
const spec = JSON.parse(await readFile(GEN_PATH, 'utf8'));
spec.paths = filterRoutes(spec.paths);
for (const [, pathItem] of Object.entries(spec.paths ?? {})) {
if (!pathItem) continue;
for (const method of ALL_METHODS) {
const operation = (<any>pathItem)[method];
if (!operation) continue;
operation.responses ??= {};
addErrorResponse(operation.responses);
}
}
await writeFile(GEN_PATH, JSON.stringify(spec));
}
process.env.ZIPLINE_OUTPUT_OPENAPI = 'true';
run(
'openapi',
step('run-prod', 'pnpm start', () => process.env.NODE_ENV === 'production'),
step('run-dev', 'pnpm dev', () => process.env.NODE_ENV !== 'production'),
step('check', async () => {
try {
await readFile(GEN_PATH);
} catch (e) {
console.error('\nSomething went wrong...', e);
throw new Error('No OpenAPI spec found at ./openapi.json');
}
}),
step('fix', fixSpec),
);
+9
View File
@@ -0,0 +1,9 @@
import { run, step } from '.';
import { lintStep } from './lint';
run(
'validate',
lintStep,
step('format', 'prettier --write --ignore-path .gitignore .'),
);
-164
View File
@@ -1,164 +0,0 @@
const next = require('next').default;
const { createServer } = require('http');
const { mkdir } = require('fs/promises');
const { extname } = require('path');
const validateConfig = require('./validateConfig');
const Logger = require('../src/lib/logger');
const readConfig = require('../src/lib/readConfig');
const mimes = require('../scripts/mimes');
const { log, getStats, getFile, migrations } = require('./util');
const { PrismaClient } = require('@prisma/client');
const { version } = require('../package.json');
const exts = require('../scripts/exts');
const serverLog = Logger.get('server');
serverLog.info(`starting zipline@${version} server`);
const dev = process.env.NODE_ENV === 'development';
(async () => {
try {
await run();
} catch (e) {
serverLog.error(e);
process.exit(1);
}
})();
async function run() {
const a = readConfig();
const config = validateConfig(a);
process.env.DATABASE_URL = config.core.database_url;
await migrations();
await mkdir(config.uploader.directory, { recursive: true });
const app = next({
dir: '.',
dev,
quiet: !dev,
hostname: config.core.host,
port: config.core.port,
});
await app.prepare();
const handle = app.getRequestHandler();
const prisma = new PrismaClient();
const srv = createServer(async (req, res) => {
if (req.url.startsWith('/r')) {
const parts = req.url.split('/');
if (!parts[2] || parts[2] === '') return;
let image = await prisma.image.findFirst({
where: {
OR: [
{ file: parts[2] },
{ invisible:{ invis: decodeURI(parts[2]) } },
],
},
select: {
mimetype: true,
id: true,
file: true,
invisible: true,
},
});
if (!image) {
const data = await getFile(config.uploader.directory, parts[2]);
if (!data) return app.render404(req, res);
const mimetype = mimes[extname(parts[2])] ?? 'application/octet-stream';
res.setHeader('Content-Type', mimetype);
res.end(data);
} else {
const data = await getFile(config.uploader.directory, image.file);
if (!data) return app.render404(req, res);
await prisma.image.update({
where: { id: image.id },
data: { views: { increment: 1 } },
});
res.setHeader('Content-Type', image.mimetype);
res.end(data);
}
} else if (req.url.startsWith(config.uploader.route)) {
const parts = req.url.split('/');
if (!parts[2] || parts[2] === '') return;
let image = await prisma.image.findFirst({
where: {
OR: [
{ file: parts[2] },
{ invisible:{ invis: decodeURI(parts[2]) } },
],
},
select: {
mimetype: true,
id: true,
file: true,
invisible: true,
embed: true,
},
});
if (!image) {
const data = await getFile(config.uploader.directory, parts[2]);
if (!data) return app.render404(req, res);
const mimetype = mimes[extname(parts[2])] ?? 'application/octet-stream';
res.setHeader('Content-Type', mimetype);
res.end(data);
} else if (image.embed) {
handle(req, res);
} else {
const ext = image.file.split('.').pop();
if (Object.keys(exts).includes(ext)) return handle(req, res);
const data = await getFile(config.uploader.directory, image.file);
if (!data) return app.render404(req, res);
await prisma.image.update({
where: { id: image.id },
data: { views: { increment: 1 } },
});
res.setHeader('Content-Type', image.mimetype);
res.end(data);
}
} else {
handle(req, res);
}
if (config.core.logger) log(req.url, res.statusCode);
});
srv.on('error', (e) => {
serverLog.error(e);
process.exit(1);
});
srv.on('listening', () => {
serverLog.info(`listening on ${config.core.host}:${config.core.port}`);
});
srv.listen(config.core.port, config.core.host ?? '0.0.0.0');
const stats = await getStats(prisma, config);
await prisma.stats.create({
data: {
data: stats,
},
});
setInterval(async () => {
const stats = await getStats(prisma, config);
await prisma.stats.create({
data: {
data: stats,
},
});
if (config.core.logger) serverLog.info('stats updated');
}, config.core.stats_interval * 1000);
}
-130
View File
@@ -1,130 +0,0 @@
const { readFile, readdir, stat } = require('fs/promises');
const { join } = require('path');
const { Migrate } = require('@prisma/migrate/dist/Migrate.js');
const Logger = require('../src/lib/logger.js');
async function migrations() {
const migrate = new Migrate('./prisma/schema.prisma');
const diagnose = await migrate.diagnoseMigrationHistory({
optInToShadowDatabase: false,
});
if (diagnose.history?.diagnostic === 'databaseIsBehind') {
Logger.get('database').info('migrating database');
await migrate.applyMigrations();
Logger.get('database').info('finished migrating database');
}
migrate.stop();
}
function log(url) {
if (url.startsWith('/_next') || url.startsWith('/__nextjs')) return;
return Logger.get('url').info(url);
}
function shouldUseYarn() {
try {
execSync('yarnpkg --version', { stdio: 'ignore' });
return true;
} catch (e) {
return false;
}
}
async function getFile(dir, file) {
try {
const data = await readFile(join(process.cwd(), dir, file));
return data;
} catch (e) {
return null;
}
}
async function sizeOfDir(directory) {
const files = await readdir(directory);
let size = 0;
for (let i = 0, L = files.length; i !== L; ++i) {
const sta = await stat(join(directory, files[i]));
size += sta.size;
}
return size;
}
function bytesToRead(bytes) {
const units = ['B', 'kB', 'MB', 'GB', 'TB', 'PB'];
let num = 0;
while (bytes > 1024) {
bytes /= 1024;
++num;
}
return `${bytes.toFixed(1)} ${units[num]}`;
}
async function getStats(prisma, config) {
const size = await sizeOfDir(join(process.cwd(), config.uploader.directory));
const byUser = await prisma.image.groupBy({
by: ['userId'],
_count: {
_all: true,
},
});
const count_users = await prisma.user.count();
const count_by_user = [];
for (let i = 0, L = byUser.length; i !== L; ++i) {
const user = await prisma.user.findFirst({
where: {
id: byUser[i].userId,
},
});
count_by_user.push({
username: user.username,
count: byUser[i]._count._all,
});
}
const count = await prisma.image.count();
const viewsCount = await prisma.image.groupBy({
by: ['views'],
_sum: {
views: true,
},
});
const typesCount = await prisma.image.groupBy({
by: ['mimetype'],
_count: {
mimetype: true,
},
});
const types_count = [];
for (let i = 0, L = typesCount.length; i !== L; ++i) types_count.push({ mimetype: typesCount[i].mimetype, count: typesCount[i]._count.mimetype });
return {
size: bytesToRead(size),
size_num: size,
count,
count_by_user: count_by_user.sort((a,b) => b.count-a.count),
count_users,
views_count: (viewsCount[0]?._sum?.views ?? 0),
types_count: types_count.sort((a,b) => b.count-a.count),
};
}
module.exports = {
migrations,
bytesToRead,
getFile,
getStats,
log,
sizeOfDir,
shouldUseYarn,
};
-40
View File
@@ -1,40 +0,0 @@
const { object, bool, string, number, boolean, array } = require('yup');
const validator = object({
core: object({
secure: bool().default(false),
secret: string().min(8).required(),
host: string().default('0.0.0.0'),
port: number().default(3000),
database_url: string().required(),
logger: boolean().default(false),
stats_interval: number().default(1800),
}).required(),
uploader: object({
route: string().default('/u'),
embed_route: string().default('/a'),
length: number().default(6),
directory: string().default('./uploads'),
admin_limit: number().default(104900000),
user_limit: number().default(104900000),
disabled_extensions: array().default([]),
}).required(),
urls: object({
route: string().default('/go'),
length: number().default(6),
}).required(),
ratelimit: object({
user: number().default(0),
admin: number().default(0),
}),
});
module.exports = function validate(config) {
try {
return validator.validateSync(config, { abortEarly: false });
} catch (e) {
if (process.env.ZIPLINE_DOCKER_BUILD) return {};
throw `${e.errors.length} errors occured\n${e.errors.map(x => '\t' + x).join('\n')}`;
}
};
+69
View File
@@ -0,0 +1,69 @@
import { ContextModalProps, ModalsProvider } from '@mantine/modals';
import { Notifications } from '@mantine/notifications';
import { Outlet } from 'react-router-dom';
import { SWRConfig } from 'swr';
import ThemeProvider from '@/components/ThemeProvider';
import { type ZiplineTheme } from '@/lib/theme';
import { type Config } from '@/lib/config/validate';
import { Button, Text } from '@mantine/core';
const AlertModal = ({ context, id, innerProps }: ContextModalProps<{ modalBody: string }>) => (
<>
<Text size='sm'>{innerProps.modalBody}</Text>
<Button fullWidth mt='md' onClick={() => context.closeModal(id)}>
OK
</Button>
</>
);
const contextModals = {
alert: AlertModal,
};
declare module '@mantine/modals' {
export interface MantineModalsOverride {
modals: typeof contextModals;
}
}
export default function Root({
themes,
defaultTheme,
}: {
themes?: ZiplineTheme[];
defaultTheme?: Config['website']['theme'];
}) {
return (
<SWRConfig
value={{
fetcher: async (url: RequestInfo | URL) => {
const res = await fetch(url);
if (!res.ok) {
const json = await res.json();
throw new Error(json.message);
}
return res.json();
},
}}
>
<ThemeProvider ssrThemes={themes} ssrDefaultTheme={defaultTheme}>
<ModalsProvider
modalProps={{
overlayProps: {
blur: 6,
},
centered: true,
}}
modals={contextModals}
>
<Notifications position='top-center' zIndex={10000000} />
<Outlet />
</ModalsProvider>
</ThemeProvider>
</SWRConfig>
);
}
@@ -0,0 +1,11 @@
import GenericError from './GenericError';
export default function DashboardErrorBoundary(props: Record<string, any>) {
return (
<GenericError
title='Dashboard Client Error'
message='Something went wrong while loading the dashboard. Please try again later, or report this issue if it persists.'
details={{ ...props, type: 'dashboard' }}
/>
);
}
+38
View File
@@ -0,0 +1,38 @@
import { Container, Paper, ScrollArea, Stack, Text, Title } from '@mantine/core';
import { useRouteError } from 'react-router-dom';
import FourOhFour from '../pages/404';
export default function GenericError({
title,
message,
details,
}: {
title?: string;
message?: string;
details?: Record<string, any>;
}) {
const routerError: any = useRouteError();
if (routerError?.status === 404) return <FourOhFour />;
const routeError = JSON.parse(JSON.stringify(routerError, Object.getOwnPropertyNames(routerError)));
console.error(routerError);
return (
<Container my='lg'>
<Stack gap='xs'>
<Title order={5}>{title || 'An error occurred'}</Title>
<Text c='dimmed'>
{message || 'Something went wrong. Please try again later, or report this issue if it persists.'}
</Text>
{details && (
<Paper withBorder px={3} py={3}>
<ScrollArea>
<pre style={{ margin: 0 }}>{JSON.stringify({ routeError, details }, null, 2)}</pre>
</ScrollArea>
</Paper>
)}
</Stack>
</Container>
);
}
+11
View File
@@ -0,0 +1,11 @@
import GenericError from './GenericError';
export default function RootErrorBoundary(props: Record<string, any>) {
return (
<GenericError
title='Dashboard Client Error'
message='Something went wrong while loading the dashboard. Please try again later, or report this issue if it persists.'
details={{ ...props, type: 'root' }}
/>
);
}
+14
View File
@@ -0,0 +1,14 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="manifest" href="manifest.json" />
<title>Zipline</title>
</head>
<body>
<div id="root"></div>
<script type="module" src="/main.tsx"></script>
</body>
</html>
+18
View File
@@ -0,0 +1,18 @@
import { StrictMode } from 'react';
import { createRoot } from 'react-dom/client';
import { RouterProvider } from 'react-router-dom';
import { router } from './routes';
import '@mantine/charts/styles.css';
import '@mantine/core/styles.css';
import '@mantine/dates/styles.css';
import '@mantine/dropzone/styles.css';
import '@mantine/notifications/styles.css';
import 'mantine-datatable/styles.css';
import './styles/global.css';
createRoot(document.getElementById('root')!).render(
<StrictMode>
<RouterProvider router={router} />
</StrictMode>,
);
+29
View File
@@ -0,0 +1,29 @@
import { useTitle } from '@/lib/hooks/useTitle';
import { Button, Center, Stack, Text, Title } from '@mantine/core';
import { IconArrowLeft } from '@tabler/icons-react';
import { Link } from 'react-router-dom';
export default function FourOhFour() {
useTitle('404');
return (
<Center h='100vh'>
<Stack>
<Title order={1}>404</Title>
<Text c='dimmed' mt='-md'>
Page not found
</Text>
<Button
component={Link}
to='/auth/login'
color='blue'
fullWidth
leftSection={<IconArrowLeft size='1rem' />}
>
Go home
</Button>
</Stack>
</Center>
);
}
+237
View File
@@ -0,0 +1,237 @@
import ExternalAuthButton from '@/components/pages/login/ExternalAuthButton';
import LocalLogin from '@/components/pages/login/LocalLogin';
import PasskeyAuthButton from '@/components/pages/login/PasskeyAuthButton';
import SecureWarningModal from '@/components/pages/login/SecureWarningModal';
import TotpModal from '@/components/pages/login/TotpModal';
import { getWebClient } from '@/lib/api/detect';
import { ApiError } from '@/lib/api/errors';
import { fetchApi } from '@/lib/fetchApi';
import useLogin from '@/lib/hooks/useLogin';
import useObjectState from '@/lib/hooks/useObjectState';
import { useTitle } from '@/lib/hooks/useTitle';
import {
Anchor,
Box,
Center,
Divider,
Group,
Image,
LoadingOverlay,
Paper,
Stack,
Text,
Title,
} from '@mantine/core';
import { useForm } from '@mantine/form';
import { showNotification } from '@mantine/notifications';
import { browserSupportsWebAuthn } from '@simplewebauthn/browser';
import {
IconBrandDiscordFilled,
IconBrandGithubFilled,
IconBrandGoogleFilled,
IconCheck,
IconCircleKeyFilled,
} from '@tabler/icons-react';
import { useEffect, useState } from 'react';
import { useNavigate } from 'react-router-dom';
import useSWR from 'swr';
import GenericError from '../../error/GenericError';
export default function Login() {
useTitle('Login');
const query = new URLSearchParams(location.search);
const navigate = useNavigate();
const { user, mutate } = useLogin();
const isHttps = window.location.protocol === 'https:';
const webClient = JSON.stringify(getWebClient());
const { data: config, error: configError, isLoading: configLoading } = useSWR('/api/server/public');
const showLocalLogin =
query.get('local') === 'true' ||
!(
config?.oauth?.bypassLocalLogin &&
Object.values(config?.oauthEnabled ?? {}).filter((x) => x === true).length > 0
);
const willRedirect =
config?.oauth?.bypassLocalLogin &&
Object.values(config?.oauthEnabled ?? {}).filter((x) => x === true).length === 1 &&
query.get('local') !== 'true';
useEffect(() => {
if (willRedirect && config) {
const provider = Object.keys(config.oauthEnabled).find(
(x) => config.oauthEnabled[x as keyof typeof config.oauthEnabled] === true,
);
if (provider) window.location.href = `/api/auth/oauth/${provider.toLowerCase()}`;
}
}, [willRedirect, config]);
const [totp, setTotp] = useObjectState({
open: false,
disabled: false,
error: '',
pin: '',
});
const [secureModal, setSecureModal] = useState(false);
const form = useForm({
initialValues: { username: '', password: '' },
validate: {
username: (v) => (v.length >= 1 ? null : 'Username is required'),
password: (v) => (v.length >= 1 ? null : 'Password is required'),
},
});
useEffect(() => {
if (user) navigate('/dashboard');
if (config?.firstSetup) navigate('/auth/setup');
}, [user, config, navigate]);
const handleLoginSubmit = async (values: any, code?: string) => {
setTotp({ disabled: true, error: '' });
const { data, error } = await fetchApi(
'/api/auth/login',
'POST',
{ ...values, code },
{ 'x-zipline-client': webClient },
);
if (error) {
if (ApiError.check(error, 1044)) {
form.setFieldError('username', 'Invalid username');
form.setFieldError('password', 'Invalid password');
} else {
setTotp('error', error.error || 'Login failed');
}
setTotp('disabled', false);
} else if (data?.totp) {
setTotp({ open: true, disabled: false });
} else {
showNotification({
message: 'Logging in...',
icon: <IconCheck size='1rem' />,
autoClose: 700,
});
mutate(data);
}
};
if (configLoading || !config) return <LoadingOverlay visible />;
if (configError) return <GenericError title='Error' message='Config load failed' details={configError} />;
const hasBg = !!config.website.loginBackground;
return (
<>
{willRedirect && !showLocalLogin && <LoadingOverlay visible />}
<TotpModal
state={totp}
onPinChange={(val) => setTotp('pin', val)}
onVerify={() => handleLoginSubmit(form.values, totp.pin)}
onCancel={() => {
setTotp('open', false);
form.reset();
}}
/>
<SecureWarningModal
opened={secureModal}
onClose={() => setSecureModal(false)}
returnHttps={config.returnHttps}
/>
{isHttps && !config.returnHttps && (
<Box pos='absolute' top={10} left='50%' style={{ transform: 'translateX(-50%)' }}>
<Text size='sm' c='red' ta='center'>
You are accessing this instance through a <b>secure</b> context but the server is not configured
to use HTTPS. Click <Anchor onClick={() => setSecureModal(true)}> here</Anchor> to learn more.
</Text>
</Box>
)}
{!isHttps && config.returnHttps && (
<Box pos='absolute' top={10} left='50%' style={{ transform: 'translateX(-50%)' }}>
<Text size='sm' c='red' ta='center'>
You are accessing this instance through an <b>insecure</b> context but the server is configured to
use HTTPS. This may cause issues when logging in. Click{' '}
<Anchor onClick={() => setSecureModal(true)}> here</Anchor> to learn more.
</Text>
</Box>
)}
<Center h='100vh'>
{hasBg && (
<Image
src={config.website.loginBackground}
pos='absolute'
inset={0}
w='100%'
h='100%'
fit='cover'
style={{ filter: config.website.loginBackgroundBlur ? 'blur(10px)' : undefined }}
/>
)}
<Paper
w='350px'
p='xl'
shadow='xl'
withBorder
pos='relative'
style={{
backgroundColor: hasBg ? 'transparent' : undefined,
backdropFilter: hasBg ? 'blur(35px)' : undefined,
}}
>
<Title order={1} ta='center' mb='md'>
<b>{config.website.title ?? 'Zipline'}</b>
</Title>
<Stack>
{showLocalLogin && (
<LocalLogin
form={form}
onSubmit={handleLoginSubmit}
loading={totp.disabled}
hasBackground={hasBg}
/>
)}
<Divider label='or' />
{config.mfa.passkeys && browserSupportsWebAuthn() && <PasskeyAuthButton onAuthSuccess={mutate} />}
<Group grow>
{config.oauthEnabled.discord && (
<ExternalAuthButton
provider='Discord'
leftSection={<IconBrandDiscordFilled stroke={4} size='1.1rem' />}
/>
)}
{config.oauthEnabled.github && (
<ExternalAuthButton provider='GitHub' leftSection={<IconBrandGithubFilled size='1.1rem' />} />
)}
{config.oauthEnabled.google && (
<ExternalAuthButton
provider='Google'
leftSection={<IconBrandGoogleFilled stroke={4} size='1.1rem' />}
/>
)}
{config.oauthEnabled.oidc && (
<ExternalAuthButton provider='OIDC' leftSection={<IconCircleKeyFilled size='1.1rem' />} />
)}
</Group>
</Stack>
</Paper>
</Center>
</>
);
}
+295
View File
@@ -0,0 +1,295 @@
import { Response } from '@/lib/api/response';
import { fetchApi } from '@/lib/fetchApi';
import { useTitle } from '@/lib/hooks/useTitle';
import {
Button,
Center,
Checkbox,
Divider,
Image,
LoadingOverlay,
Paper,
PasswordInput,
Stack,
Text,
TextInput,
Title,
} from '@mantine/core';
import { useForm } from '@mantine/form';
import { notifications, showNotification } from '@mantine/notifications';
import { IconLogin, IconPlus, IconUserPlus, IconX } from '@tabler/icons-react';
import { useEffect, useState } from 'react';
import { Link, useLocation, useNavigate } from 'react-router-dom';
import useSWR, { mutate } from 'swr';
import GenericError from '../../error/GenericError';
import { getWebClient } from '@/lib/api/detect';
import { ApiError } from '@/lib/api/errors';
export function Component() {
useTitle('Register');
const location = useLocation();
const navigate = useNavigate();
const [loading, setLoading] = useState(true);
const {
data: config,
error: configError,
isLoading: configLoading,
} = useSWR<Response['/api/server/public']>('/api/server/public', {
revalidateOnFocus: false,
revalidateOnReconnect: false,
refreshWhenHidden: false,
revalidateIfStale: false,
});
const code = new URLSearchParams(location.search).get('code') ?? undefined;
const {
data: invite,
error: inviteError,
isLoading: inviteLoading,
} = useSWR<Response['/api/auth/invites/web']>(
location.search.includes('code') ? `/api/auth/invites/web${location.search}` : null,
{
revalidateOnFocus: false,
revalidateOnReconnect: false,
refreshWhenHidden: false,
revalidateIfStale: false,
},
);
const form = useForm({
initialValues: {
username: '',
password: '',
tos: false,
},
validate: {
username: (value) => (value.length >= 1 ? null : 'Username is required'),
password: (value) => (value.length >= 1 ? null : 'Password is required'),
},
enhanceGetInputProps: ({ field }) => ({
name: field,
}),
});
useEffect(() => {
(async () => {
const res = await fetch('/api/user');
if (res.ok) {
navigate('/dashboard');
} else {
setLoading(false);
}
})();
}, []);
useEffect(() => {
if (!config) return;
if (!config?.features.userRegistration && !code) {
navigate('/auth/login');
}
}, [code, config]);
const onSubmit = async (values: typeof form.values) => {
const { username, password, tos } = values;
if (tos === false && config!.website.tos) {
form.setFieldError('tos', 'You must agree to the Terms of Service to continue');
return;
}
const { data, error } = await fetchApi(
'/api/auth/register',
'POST',
{
username,
password,
code,
},
{
'x-zipline-client': JSON.stringify(getWebClient()),
},
);
if (error) {
if (ApiError.check(error, 1039)) {
form.setFieldError('username', 'Username is taken');
} else {
notifications.show({
title: 'Failed to register',
message: error.error,
color: 'red',
icon: <IconX size='1rem' />,
});
}
} else {
notifications.show({
title: 'Complete!',
message: `Your "${data?.user?.username}" account has been created.`,
color: 'green',
icon: <IconPlus size='1rem' />,
});
mutate('/api/user');
navigate('/dashboard');
}
};
if (loading || configLoading) return <LoadingOverlay visible />;
if (!config || configError) {
return (
<GenericError
title='Error loading configuration'
message='Could not load server configuration...'
details={configError}
/>
);
}
if (code && inviteError) {
if (inviteError) {
showNotification({
id: 'invalid-invite',
message: 'Invalid or expired invite. Please try again later.',
color: 'red',
});
navigate('/auth/login');
return null;
}
if (inviteLoading) return <LoadingOverlay visible />;
}
return (
<Center h='100vh'>
{config.website.loginBackground && (
<Image
src={config.website.loginBackground}
alt='Background'
style={{
position: 'absolute',
top: 0,
left: 0,
right: 0,
bottom: 0,
width: '100%',
height: '100%',
objectFit: 'cover',
...(config.website.loginBackgroundBlur && { filter: 'blur(10px)' }),
}}
/>
)}
<Paper
w='350px'
p='xl'
shadow='xl'
withBorder
style={{
backgroundColor: config.website.loginBackground ? 'rgba(0, 0, 0, 0)' : undefined,
backdropFilter: config.website.loginBackgroundBlur ? 'blur(35px)' : undefined,
}}
>
<div style={{ width: '100%', overflowWrap: 'break-word' }}>
<Title
order={1}
ta='center'
style={{
whiteSpace: 'normal',
fontSize: `clamp(20px, ${Math.max(50 - (config.website.title?.length ?? 0) / 2, 20)}px, 50px)`,
}}
>
<b>{config.website.title ?? 'Zipline'}</b>
</Title>
</div>
{invite && (
<Text ta='center' size='sm' c='dimmed'>
Youve been invited to join <b>{config?.website?.title ?? 'Zipline'}</b>
{invite.inviter && (
<>
{' '}
by <b>{invite.inviter.username}</b>
</>
)}
</Text>
)}
<form onSubmit={form.onSubmit(onSubmit)}>
<Stack my='sm'>
<TextInput
size='md'
placeholder='Enter your username...'
autoComplete='username'
styles={{
input: {
backgroundColor: config.website.loginBackground ? 'transparent' : undefined,
},
}}
{...form.getInputProps('username', { withError: true })}
/>
<PasswordInput
size='md'
placeholder='Enter your password...'
autoComplete='new-password'
styles={{
input: {
backgroundColor: config.website.loginBackground ? 'transparent' : undefined,
},
}}
{...form.getInputProps('password')}
/>
{config.website.tos && (
<Checkbox
label={
<Text size='xs'>
I agree to the{' '}
<Link to='/auth/tos' target='_blank'>
Terms of Service
</Link>
</Text>
}
required
{...form.getInputProps('tos', { type: 'checkbox' })}
/>
)}
<Button
size='md'
fullWidth
type='submit'
variant={config.website.loginBackground ? 'outline' : 'filled'}
leftSection={<IconUserPlus size='1rem' />}
>
Register
</Button>
</Stack>
</form>
<Stack my='xs'>
<Divider label='or' />
<Button
component={Link}
to='/auth/login'
size='md'
fullWidth
variant='outline'
leftSection={<IconLogin size='1rem' />}
>
Login
</Button>
</Stack>
</Paper>
</Center>
);
}
Component.displayName = 'Register';
+254
View File
@@ -0,0 +1,254 @@
import { type Response } from '@/lib/api/response';
import { fetchApi } from '@/lib/fetchApi';
import { useTitle } from '@/lib/hooks/useTitle';
import {
Anchor,
Button,
Code,
Group,
Paper,
PasswordInput,
SimpleGrid,
Stack,
Stepper,
Text,
TextInput,
Title,
} from '@mantine/core';
import { useForm } from '@mantine/form';
import { notifications } from '@mantine/notifications';
import { IconArrowBackUp, IconArrowForwardUp, IconCheck, IconX } from '@tabler/icons-react';
import { useState } from 'react';
import { redirect, useNavigate } from 'react-router-dom';
import { mutate } from 'swr';
function LinkToDoc({ href, title, children }: { href: string; title: string; children: React.ReactNode }) {
return (
<Text>
<Anchor href={href} target='_blank' rel='noopener noreferrer'>
{title}
</Anchor>{' '}
{children}
</Text>
);
}
export async function loader() {
const res = await fetch('/api/server/public');
if (!res.ok) {
throw new Response('Failed to fetch server settings', { status: res.status });
}
const data = await res.json();
if (!data.firstSetup) return redirect('/auth/login');
return {};
}
export function Component() {
useTitle('Setup');
const navigate = useNavigate();
const [active, setActive] = useState(0);
const nextStep = () => setActive((current) => (current < 3 ? current + 1 : current));
const prevStep = () => setActive((current) => (current > 0 ? current - 1 : current));
const [loading, setLoading] = useState(false);
const form = useForm({
initialValues: {
username: '',
password: '',
},
validate: {
username: (value) => (value.length >= 1 ? null : 'Username is required'),
password: (value) => (value.length >= 1 ? null : 'Password is required'),
},
enhanceGetInputProps: ({ field }) => ({
name: field,
}),
});
const onSubmit = async (values: typeof form.values) => {
setLoading(true);
const { error } = await fetchApi('/api/setup', 'POST', {
username: values.username,
password: values.password,
});
if (error) {
notifications.show({
title: 'Error',
message: error.error,
color: 'red',
icon: <IconX size='1rem' />,
});
setLoading(false);
setActive(2);
} else {
notifications.show({
title: 'Setup complete!',
message: 'Logging in to new user...',
color: 'green',
loading: true,
});
const { data, error } = await fetchApi<Response['/api/auth/login']>('/api/auth/login', 'POST', {
username: values.username,
password: values.password,
});
if (error) {
notifications.show({
title: 'Error',
message: error.error,
color: 'red',
icon: <IconX size='1rem' />,
});
setLoading(false);
setActive(2);
} else {
mutate('/api/user', data as Response['/api/user']);
navigate('/dashboard');
}
}
};
return (
<>
<Paper withBorder p='xs' m='sm'>
<Stepper active={active} onStepClick={setActive} m='md'>
<Stepper.Step label='Welcome!' description='Setup Zipline'>
<Title>Welcome to Zipline!</Title>
<SimpleGrid spacing='md' cols={{ base: 1, sm: 1 }}>
<Paper withBorder p='sm' my='sm' h='100%'>
<Title order={2}>Documentation</Title>
<Text>Here are a couple of useful documentation links to get you started with Zipline:</Text>
<Stack mt='xs'>
<LinkToDoc href='https://zipline.diced.sh/docs/config' title='Configuration'>
Configuring Zipline to your needs
</LinkToDoc>
<LinkToDoc href='https://zipline.diced.sh/docs/migrate' title='Migrate from v3 to v4'>
Upgrading from a previous version of Zipline
</LinkToDoc>
</Stack>
</Paper>
<Paper withBorder p='sm' my='sm' h='100%'>
<Title order={2}>Configuration</Title>
<Text>
Most of Zipline&apos;s configuration is now managed through the dashboard. Once you login as
a super-admin, you can click on your username in the top right corner and select
&quot;Server Settings&quot; to configure your instance. The only exception to this is a few
sensitive environment variables that must be set in order for Zipline to run. To change
this, depending on the setup, you can either edit the <Code>.env</Code> or{' '}
<Code>docker-compose.yml</Code> file.
</Text>
<Text>
To see all of the available environment variables, please refer to the documentation{' '}
<Anchor
href='https://zipline.diced.sh/docs/config'
target='_blank'
rel='noopener noreferrer'
>
here.
</Anchor>
</Text>
</Paper>
</SimpleGrid>
<Button
mt='xl'
fullWidth
rightSection={<IconArrowForwardUp size='1.25rem' />}
size='lg'
variant='default'
onClick={nextStep}
>
Continue
</Button>
</Stepper.Step>
<Stepper.Step label='Create user' description='Create a super-admin account'>
<Stack gap='lg'>
<Title order={2}>Create your super-admin account</Title>
<TextInput
label='Username'
placeholder='Enter a username...'
autoComplete='username'
{...form.getInputProps('username')}
/>
<PasswordInput
label='Password'
placeholder='Enter a password...'
autoComplete='new-password'
{...form.getInputProps('password')}
/>
</Stack>
<Group justify='space-between' my='lg'>
<Button
leftSection={<IconArrowBackUp size='1.25rem' />}
size='lg'
variant='default'
onClick={prevStep}
>
Back
</Button>
<Button
rightSection={<IconArrowForwardUp size='1.25rem' />}
size='lg'
variant='default'
onClick={nextStep}
disabled={!form.isValid()}
>
Continue
</Button>
</Group>
</Stepper.Step>
<Stepper.Completed>
<Title order={2}>Setup complete!</Title>
<Text>
Clicking &quot;Finish&quot; below will create your super-admin account and log you in. You will
be redirected to the dashboard shortly after that.
</Text>
<Group justify='space-between' my='lg'>
<Button
leftSection={<IconArrowBackUp size='1.25rem' />}
size='lg'
variant='default'
onClick={prevStep}
loading={loading}
>
Back
</Button>
<Button
rightSection={<IconCheck size='1.25rem' />}
size='lg'
variant='default'
loading={loading}
onClick={() => form.onSubmit(onSubmit)()}
>
Finish
</Button>
</Group>
</Stepper.Completed>
</Stepper>
</Paper>
</>
);
}
Component.displayName = 'Setup';

Some files were not shown because too many files have changed in this diff Show More