initial commit
A.dockerignore
| @@ -0,0 +1,44 @@ | |||
|---|---|---|---|
| 1 | + | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. | |
| 2 | + | ||
| 3 | + | # dependencies | |
| 4 | + | /node_modules | |
| 5 | + | /.pnp | |
| 6 | + | .pnp.js | |
| 7 | + | ||
| 8 | + | # testing | |
| 9 | + | /coverage | |
| 10 | + | ||
| 11 | + | # next.js | |
| 12 | + | /.next/ | |
| 13 | + | /out/ | |
| 14 | + | ||
| 15 | + | # production | |
| 16 | + | /build | |
| 17 | + | ||
| 18 | + | # misc | |
| 19 | + | .DS_Store | |
| 20 | + | *.pem | |
| 21 | + | ||
| 22 | + | # debug | |
| 23 | + | npm-debug.log* | |
| 24 | + | yarn-debug.log* | |
| 25 | + | yarn-error.log* | |
| 26 | + | ||
| 27 | + | # local env files | |
| 28 | + | .env.local | |
| 29 | + | .env.development.local | |
| 30 | + | .env.test.local | |
| 31 | + | .env.production.local | |
| 32 | + | ||
| 33 | + | # vercel | |
| 34 | + | .vercel | |
| 35 | + | ||
| 36 | + | **/*.trace | |
| 37 | + | **/*.zip | |
| 38 | + | **/*.tar.gz | |
| 39 | + | **/*.tgz | |
| 40 | + | **/*.log | |
| 41 | + | package-lock.json | |
| 42 | + | **/*.bun | |
| 43 | + | *.sqlite | |
| 44 | + | assets/dist | |
M.gitignore
| @@ -39,4 +39,6 @@ yarn-error.log* | |||
|---|---|---|---|
| 39 | 39 | **/*.tgz | |
| 40 | 40 | **/*.log | |
| 41 | 41 | package-lock.json | |
| 42 | - | **/*.bun | |
| 42 | + | **/*.bun | |
| 43 | + | *.sqlite | |
| 44 | + | assets/dist | |
ADockerfile
| @@ -0,0 +1,8 @@ | |||
|---|---|---|---|
| 1 | + | FROM debian:12-slim | |
| 2 | + | RUN apt-get update && apt-get install -y curl unzip | |
| 3 | + | RUN curl -fsSL https://bun.sh/install | bash | |
| 4 | + | WORKDIR /app | |
| 5 | + | COPY . . | |
| 6 | + | RUN /root/.bun/bin/bun install | |
| 7 | + | EXPOSE 3000 | |
| 8 | + | CMD ["/root/.bun/bin/bun", "run", "prod"] | |
ALICENSE
| @@ -0,0 +1,661 @@ | |||
|---|---|---|---|
| 1 | + | GNU AFFERO GENERAL PUBLIC LICENSE | |
| 2 | + | Version 3, 19 November 2007 | |
| 3 | + | ||
| 4 | + | Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/> | |
| 5 | + | Everyone is permitted to copy and distribute verbatim copies | |
| 6 | + | of this license document, but changing it is not allowed. | |
| 7 | + | ||
| 8 | + | Preamble | |
| 9 | + | ||
| 10 | + | The GNU Affero General Public License is a free, copyleft license for | |
| 11 | + | software and other kinds of works, specifically designed to ensure | |
| 12 | + | cooperation with the community in the case of network server software. | |
| 13 | + | ||
| 14 | + | The licenses for most software and other practical works are designed | |
| 15 | + | to take away your freedom to share and change the works. By contrast, | |
| 16 | + | our General Public Licenses are intended to guarantee your freedom to | |
| 17 | + | share and change all versions of a program--to make sure it remains free | |
| 18 | + | software for all its users. | |
| 19 | + | ||
| 20 | + | When we speak of free software, we are referring to freedom, not | |
| 21 | + | price. Our General Public Licenses are designed to make sure that you | |
| 22 | + | have the freedom to distribute copies of free software (and charge for | |
| 23 | + | them if you wish), that you receive source code or can get it if you | |
| 24 | + | want it, that you can change the software or use pieces of it in new | |
| 25 | + | free programs, and that you know you can do these things. | |
| 26 | + | ||
| 27 | + | Developers that use our General Public Licenses protect your rights | |
| 28 | + | with two steps: (1) assert copyright on the software, and (2) offer | |
| 29 | + | you this License which gives you legal permission to copy, distribute | |
| 30 | + | and/or modify the software. | |
| 31 | + | ||
| 32 | + | A secondary benefit of defending all users' freedom is that | |
| 33 | + | improvements made in alternate versions of the program, if they | |
| 34 | + | receive widespread use, become available for other developers to | |
| 35 | + | incorporate. Many developers of free software are heartened and | |
| 36 | + | encouraged by the resulting cooperation. However, in the case of | |
| 37 | + | software used on network servers, this result may fail to come about. | |
| 38 | + | The GNU General Public License permits making a modified version and | |
| 39 | + | letting the public access it on a server without ever releasing its | |
| 40 | + | source code to the public. | |
| 41 | + | ||
| 42 | + | The GNU Affero General Public License is designed specifically to | |
| 43 | + | ensure that, in such cases, the modified source code becomes available | |
| 44 | + | to the community. It requires the operator of a network server to | |
| 45 | + | provide the source code of the modified version running there to the | |
| 46 | + | users of that server. Therefore, public use of a modified version, on | |
| 47 | + | a publicly accessible server, gives the public access to the source | |
| 48 | + | code of the modified version. | |
| 49 | + | ||
| 50 | + | An older license, called the Affero General Public License and | |
| 51 | + | published by Affero, was designed to accomplish similar goals. This is | |
| 52 | + | a different license, not a version of the Affero GPL, but Affero has | |
| 53 | + | released a new version of the Affero GPL which permits relicensing under | |
| 54 | + | this license. | |
| 55 | + | ||
| 56 | + | The precise terms and conditions for copying, distribution and | |
| 57 | + | modification follow. | |
| 58 | + | ||
| 59 | + | TERMS AND CONDITIONS | |
| 60 | + | ||
| 61 | + | 0. Definitions. | |
| 62 | + | ||
| 63 | + | "This License" refers to version 3 of the GNU Affero General Public License. | |
| 64 | + | ||
| 65 | + | "Copyright" also means copyright-like laws that apply to other kinds of | |
| 66 | + | works, such as semiconductor masks. | |
| 67 | + | ||
| 68 | + | "The Program" refers to any copyrightable work licensed under this | |
| 69 | + | License. Each licensee is addressed as "you". "Licensees" and | |
| 70 | + | "recipients" may be individuals or organizations. | |
| 71 | + | ||
| 72 | + | To "modify" a work means to copy from or adapt all or part of the work | |
| 73 | + | in a fashion requiring copyright permission, other than the making of an | |
| 74 | + | exact copy. The resulting work is called a "modified version" of the | |
| 75 | + | earlier work or a work "based on" the earlier work. | |
| 76 | + | ||
| 77 | + | A "covered work" means either the unmodified Program or a work based | |
| 78 | + | on the Program. | |
| 79 | + | ||
| 80 | + | To "propagate" a work means to do anything with it that, without | |
| 81 | + | permission, would make you directly or secondarily liable for | |
| 82 | + | infringement under applicable copyright law, except executing it on a | |
| 83 | + | computer or modifying a private copy. Propagation includes copying, | |
| 84 | + | distribution (with or without modification), making available to the | |
| 85 | + | public, and in some countries other activities as well. | |
| 86 | + | ||
| 87 | + | To "convey" a work means any kind of propagation that enables other | |
| 88 | + | parties to make or receive copies. Mere interaction with a user through | |
| 89 | + | a computer network, with no transfer of a copy, is not conveying. | |
| 90 | + | ||
| 91 | + | An interactive user interface displays "Appropriate Legal Notices" | |
| 92 | + | to the extent that it includes a convenient and prominently visible | |
| 93 | + | feature that (1) displays an appropriate copyright notice, and (2) | |
| 94 | + | tells the user that there is no warranty for the work (except to the | |
| 95 | + | extent that warranties are provided), that licensees may convey the | |
| 96 | + | work under this License, and how to view a copy of this License. If | |
| 97 | + | the interface presents a list of user commands or options, such as a | |
| 98 | + | menu, a prominent item in the list meets this criterion. | |
| 99 | + | ||
| 100 | + | 1. Source Code. | |
| 101 | + | ||
| 102 | + | The "source code" for a work means the preferred form of the work | |
| 103 | + | for making modifications to it. "Object code" means any non-source | |
| 104 | + | form of a work. | |
| 105 | + | ||
| 106 | + | A "Standard Interface" means an interface that either is an official | |
| 107 | + | standard defined by a recognized standards body, or, in the case of | |
| 108 | + | interfaces specified for a particular programming language, one that | |
| 109 | + | is widely used among developers working in that language. | |
| 110 | + | ||
| 111 | + | The "System Libraries" of an executable work include anything, other | |
| 112 | + | than the work as a whole, that (a) is included in the normal form of | |
| 113 | + | packaging a Major Component, but which is not part of that Major | |
| 114 | + | Component, and (b) serves only to enable use of the work with that | |
| 115 | + | Major Component, or to implement a Standard Interface for which an | |
| 116 | + | implementation is available to the public in source code form. A | |
| 117 | + | "Major Component", in this context, means a major essential component | |
| 118 | + | (kernel, window system, and so on) of the specific operating system | |
| 119 | + | (if any) on which the executable work runs, or a compiler used to | |
| 120 | + | produce the work, or an object code interpreter used to run it. | |
| 121 | + | ||
| 122 | + | The "Corresponding Source" for a work in object code form means all | |
| 123 | + | the source code needed to generate, install, and (for an executable | |
| 124 | + | work) run the object code and to modify the work, including scripts to | |
| 125 | + | control those activities. However, it does not include the work's | |
| 126 | + | System Libraries, or general-purpose tools or generally available free | |
| 127 | + | programs which are used unmodified in performing those activities but | |
| 128 | + | which are not part of the work. For example, Corresponding Source | |
| 129 | + | includes interface definition files associated with source files for | |
| 130 | + | the work, and the source code for shared libraries and dynamically | |
| 131 | + | linked subprograms that the work is specifically designed to require, | |
| 132 | + | such as by intimate data communication or control flow between those | |
| 133 | + | subprograms and other parts of the work. | |
| 134 | + | ||
| 135 | + | The Corresponding Source need not include anything that users | |
| 136 | + | can regenerate automatically from other parts of the Corresponding | |
| 137 | + | Source. | |
| 138 | + | ||
| 139 | + | The Corresponding Source for a work in source code form is that | |
| 140 | + | same work. | |
| 141 | + | ||
| 142 | + | 2. Basic Permissions. | |
| 143 | + | ||
| 144 | + | All rights granted under this License are granted for the term of | |
| 145 | + | copyright on the Program, and are irrevocable provided the stated | |
| 146 | + | conditions are met. This License explicitly affirms your unlimited | |
| 147 | + | permission to run the unmodified Program. The output from running a | |
| 148 | + | covered work is covered by this License only if the output, given its | |
| 149 | + | content, constitutes a covered work. This License acknowledges your | |
| 150 | + | rights of fair use or other equivalent, as provided by copyright law. | |
| 151 | + | ||
| 152 | + | You may make, run and propagate covered works that you do not | |
| 153 | + | convey, without conditions so long as your license otherwise remains | |
| 154 | + | in force. You may convey covered works to others for the sole purpose | |
| 155 | + | of having them make modifications exclusively for you, or provide you | |
| 156 | + | with facilities for running those works, provided that you comply with | |
| 157 | + | the terms of this License in conveying all material for which you do | |
| 158 | + | not control copyright. Those thus making or running the covered works | |
| 159 | + | for you must do so exclusively on your behalf, under your direction | |
| 160 | + | and control, on terms that prohibit them from making any copies of | |
| 161 | + | your copyrighted material outside their relationship with you. | |
| 162 | + | ||
| 163 | + | Conveying under any other circumstances is permitted solely under | |
| 164 | + | the conditions stated below. Sublicensing is not allowed; section 10 | |
| 165 | + | makes it unnecessary. | |
| 166 | + | ||
| 167 | + | 3. Protecting Users' Legal Rights From Anti-Circumvention Law. | |
| 168 | + | ||
| 169 | + | No covered work shall be deemed part of an effective technological | |
| 170 | + | measure under any applicable law fulfilling obligations under article | |
| 171 | + | 11 of the WIPO copyright treaty adopted on 20 December 1996, or | |
| 172 | + | similar laws prohibiting or restricting circumvention of such | |
| 173 | + | measures. | |
| 174 | + | ||
| 175 | + | When you convey a covered work, you waive any legal power to forbid | |
| 176 | + | circumvention of technological measures to the extent such circumvention | |
| 177 | + | is effected by exercising rights under this License with respect to | |
| 178 | + | the covered work, and you disclaim any intention to limit operation or | |
| 179 | + | modification of the work as a means of enforcing, against the work's | |
| 180 | + | users, your or third parties' legal rights to forbid circumvention of | |
| 181 | + | technological measures. | |
| 182 | + | ||
| 183 | + | 4. Conveying Verbatim Copies. | |
| 184 | + | ||
| 185 | + | You may convey verbatim copies of the Program's source code as you | |
| 186 | + | receive it, in any medium, provided that you conspicuously and | |
| 187 | + | appropriately publish on each copy an appropriate copyright notice; | |
| 188 | + | keep intact all notices stating that this License and any | |
| 189 | + | non-permissive terms added in accord with section 7 apply to the code; | |
| 190 | + | keep intact all notices of the absence of any warranty; and give all | |
| 191 | + | recipients a copy of this License along with the Program. | |
| 192 | + | ||
| 193 | + | You may charge any price or no price for each copy that you convey, | |
| 194 | + | and you may offer support or warranty protection for a fee. | |
| 195 | + | ||
| 196 | + | 5. Conveying Modified Source Versions. | |
| 197 | + | ||
| 198 | + | You may convey a work based on the Program, or the modifications to | |
| 199 | + | produce it from the Program, in the form of source code under the | |
| 200 | + | terms of section 4, provided that you also meet all of these conditions: | |
| 201 | + | ||
| 202 | + | a) The work must carry prominent notices stating that you modified | |
| 203 | + | it, and giving a relevant date. | |
| 204 | + | ||
| 205 | + | b) The work must carry prominent notices stating that it is | |
| 206 | + | released under this License and any conditions added under section | |
| 207 | + | 7. This requirement modifies the requirement in section 4 to | |
| 208 | + | "keep intact all notices". | |
| 209 | + | ||
| 210 | + | c) You must license the entire work, as a whole, under this | |
| 211 | + | License to anyone who comes into possession of a copy. This | |
| 212 | + | License will therefore apply, along with any applicable section 7 | |
| 213 | + | additional terms, to the whole of the work, and all its parts, | |
| 214 | + | regardless of how they are packaged. This License gives no | |
| 215 | + | permission to license the work in any other way, but it does not | |
| 216 | + | invalidate such permission if you have separately received it. | |
| 217 | + | ||
| 218 | + | d) If the work has interactive user interfaces, each must display | |
| 219 | + | Appropriate Legal Notices; however, if the Program has interactive | |
| 220 | + | interfaces that do not display Appropriate Legal Notices, your | |
| 221 | + | work need not make them do so. | |
| 222 | + | ||
| 223 | + | A compilation of a covered work with other separate and independent | |
| 224 | + | works, which are not by their nature extensions of the covered work, | |
| 225 | + | and which are not combined with it such as to form a larger program, | |
| 226 | + | in or on a volume of a storage or distribution medium, is called an | |
| 227 | + | "aggregate" if the compilation and its resulting copyright are not | |
| 228 | + | used to limit the access or legal rights of the compilation's users | |
| 229 | + | beyond what the individual works permit. Inclusion of a covered work | |
| 230 | + | in an aggregate does not cause this License to apply to the other | |
| 231 | + | parts of the aggregate. | |
| 232 | + | ||
| 233 | + | 6. Conveying Non-Source Forms. | |
| 234 | + | ||
| 235 | + | You may convey a covered work in object code form under the terms | |
| 236 | + | of sections 4 and 5, provided that you also convey the | |
| 237 | + | machine-readable Corresponding Source under the terms of this License, | |
| 238 | + | in one of these ways: | |
| 239 | + | ||
| 240 | + | a) Convey the object code in, or embodied in, a physical product | |
| 241 | + | (including a physical distribution medium), accompanied by the | |
| 242 | + | Corresponding Source fixed on a durable physical medium | |
| 243 | + | customarily used for software interchange. | |
| 244 | + | ||
| 245 | + | b) Convey the object code in, or embodied in, a physical product | |
| 246 | + | (including a physical distribution medium), accompanied by a | |
| 247 | + | written offer, valid for at least three years and valid for as | |
| 248 | + | long as you offer spare parts or customer support for that product | |
| 249 | + | model, to give anyone who possesses the object code either (1) a | |
| 250 | + | copy of the Corresponding Source for all the software in the | |
| 251 | + | product that is covered by this License, on a durable physical | |
| 252 | + | medium customarily used for software interchange, for a price no | |
| 253 | + | more than your reasonable cost of physically performing this | |
| 254 | + | conveying of source, or (2) access to copy the | |
| 255 | + | Corresponding Source from a network server at no charge. | |
| 256 | + | ||
| 257 | + | c) Convey individual copies of the object code with a copy of the | |
| 258 | + | written offer to provide the Corresponding Source. This | |
| 259 | + | alternative is allowed only occasionally and noncommercially, and | |
| 260 | + | only if you received the object code with such an offer, in accord | |
| 261 | + | with subsection 6b. | |
| 262 | + | ||
| 263 | + | d) Convey the object code by offering access from a designated | |
| 264 | + | place (gratis or for a charge), and offer equivalent access to the | |
| 265 | + | Corresponding Source in the same way through the same place at no | |
| 266 | + | further charge. You need not require recipients to copy the | |
| 267 | + | Corresponding Source along with the object code. If the place to | |
| 268 | + | copy the object code is a network server, the Corresponding Source | |
| 269 | + | may be on a different server (operated by you or a third party) | |
| 270 | + | that supports equivalent copying facilities, provided you maintain | |
| 271 | + | clear directions next to the object code saying where to find the | |
| 272 | + | Corresponding Source. Regardless of what server hosts the | |
| 273 | + | Corresponding Source, you remain obligated to ensure that it is | |
| 274 | + | available for as long as needed to satisfy these requirements. | |
| 275 | + | ||
| 276 | + | e) Convey the object code using peer-to-peer transmission, provided | |
| 277 | + | you inform other peers where the object code and Corresponding | |
| 278 | + | Source of the work are being offered to the general public at no | |
| 279 | + | charge under subsection 6d. | |
| 280 | + | ||
| 281 | + | A separable portion of the object code, whose source code is excluded | |
| 282 | + | from the Corresponding Source as a System Library, need not be | |
| 283 | + | included in conveying the object code work. | |
| 284 | + | ||
| 285 | + | A "User Product" is either (1) a "consumer product", which means any | |
| 286 | + | tangible personal property which is normally used for personal, family, | |
| 287 | + | or household purposes, or (2) anything designed or sold for incorporation | |
| 288 | + | into a dwelling. In determining whether a product is a consumer product, | |
| 289 | + | doubtful cases shall be resolved in favor of coverage. For a particular | |
| 290 | + | product received by a particular user, "normally used" refers to a | |
| 291 | + | typical or common use of that class of product, regardless of the status | |
| 292 | + | of the particular user or of the way in which the particular user | |
| 293 | + | actually uses, or expects or is expected to use, the product. A product | |
| 294 | + | is a consumer product regardless of whether the product has substantial | |
| 295 | + | commercial, industrial or non-consumer uses, unless such uses represent | |
| 296 | + | the only significant mode of use of the product. | |
| 297 | + | ||
| 298 | + | "Installation Information" for a User Product means any methods, | |
| 299 | + | procedures, authorization keys, or other information required to install | |
| 300 | + | and execute modified versions of a covered work in that User Product from | |
| 301 | + | a modified version of its Corresponding Source. The information must | |
| 302 | + | suffice to ensure that the continued functioning of the modified object | |
| 303 | + | code is in no case prevented or interfered with solely because | |
| 304 | + | modification has been made. | |
| 305 | + | ||
| 306 | + | If you convey an object code work under this section in, or with, or | |
| 307 | + | specifically for use in, a User Product, and the conveying occurs as | |
| 308 | + | part of a transaction in which the right of possession and use of the | |
| 309 | + | User Product is transferred to the recipient in perpetuity or for a | |
| 310 | + | fixed term (regardless of how the transaction is characterized), the | |
| 311 | + | Corresponding Source conveyed under this section must be accompanied | |
| 312 | + | by the Installation Information. But this requirement does not apply | |
| 313 | + | if neither you nor any third party retains the ability to install | |
| 314 | + | modified object code on the User Product (for example, the work has | |
| 315 | + | been installed in ROM). | |
| 316 | + | ||
| 317 | + | The requirement to provide Installation Information does not include a | |
| 318 | + | requirement to continue to provide support service, warranty, or updates | |
| 319 | + | for a work that has been modified or installed by the recipient, or for | |
| 320 | + | the User Product in which it has been modified or installed. Access to a | |
| 321 | + | network may be denied when the modification itself materially and | |
| 322 | + | adversely affects the operation of the network or violates the rules and | |
| 323 | + | protocols for communication across the network. | |
| 324 | + | ||
| 325 | + | Corresponding Source conveyed, and Installation Information provided, | |
| 326 | + | in accord with this section must be in a format that is publicly | |
| 327 | + | documented (and with an implementation available to the public in | |
| 328 | + | source code form), and must require no special password or key for | |
| 329 | + | unpacking, reading or copying. | |
| 330 | + | ||
| 331 | + | 7. Additional Terms. | |
| 332 | + | ||
| 333 | + | "Additional permissions" are terms that supplement the terms of this | |
| 334 | + | License by making exceptions from one or more of its conditions. | |
| 335 | + | Additional permissions that are applicable to the entire Program shall | |
| 336 | + | be treated as though they were included in this License, to the extent | |
| 337 | + | that they are valid under applicable law. If additional permissions | |
| 338 | + | apply only to part of the Program, that part may be used separately | |
| 339 | + | under those permissions, but the entire Program remains governed by | |
| 340 | + | this License without regard to the additional permissions. | |
| 341 | + | ||
| 342 | + | When you convey a copy of a covered work, you may at your option | |
| 343 | + | remove any additional permissions from that copy, or from any part of | |
| 344 | + | it. (Additional permissions may be written to require their own | |
| 345 | + | removal in certain cases when you modify the work.) You may place | |
| 346 | + | additional permissions on material, added by you to a covered work, | |
| 347 | + | for which you have or can give appropriate copyright permission. | |
| 348 | + | ||
| 349 | + | Notwithstanding any other provision of this License, for material you | |
| 350 | + | add to a covered work, you may (if authorized by the copyright holders of | |
| 351 | + | that material) supplement the terms of this License with terms: | |
| 352 | + | ||
| 353 | + | a) Disclaiming warranty or limiting liability differently from the | |
| 354 | + | terms of sections 15 and 16 of this License; or | |
| 355 | + | ||
| 356 | + | b) Requiring preservation of specified reasonable legal notices or | |
| 357 | + | author attributions in that material or in the Appropriate Legal | |
| 358 | + | Notices displayed by works containing it; or | |
| 359 | + | ||
| 360 | + | c) Prohibiting misrepresentation of the origin of that material, or | |
| 361 | + | requiring that modified versions of such material be marked in | |
| 362 | + | reasonable ways as different from the original version; or | |
| 363 | + | ||
| 364 | + | d) Limiting the use for publicity purposes of names of licensors or | |
| 365 | + | authors of the material; or | |
| 366 | + | ||
| 367 | + | e) Declining to grant rights under trademark law for use of some | |
| 368 | + | trade names, trademarks, or service marks; or | |
| 369 | + | ||
| 370 | + | f) Requiring indemnification of licensors and authors of that | |
| 371 | + | material by anyone who conveys the material (or modified versions of | |
| 372 | + | it) with contractual assumptions of liability to the recipient, for | |
| 373 | + | any liability that these contractual assumptions directly impose on | |
| 374 | + | those licensors and authors. | |
| 375 | + | ||
| 376 | + | All other non-permissive additional terms are considered "further | |
| 377 | + | restrictions" within the meaning of section 10. If the Program as you | |
| 378 | + | received it, or any part of it, contains a notice stating that it is | |
| 379 | + | governed by this License along with a term that is a further | |
| 380 | + | restriction, you may remove that term. If a license document contains | |
| 381 | + | a further restriction but permits relicensing or conveying under this | |
| 382 | + | License, you may add to a covered work material governed by the terms | |
| 383 | + | of that license document, provided that the further restriction does | |
| 384 | + | not survive such relicensing or conveying. | |
| 385 | + | ||
| 386 | + | If you add terms to a covered work in accord with this section, you | |
| 387 | + | must place, in the relevant source files, a statement of the | |
| 388 | + | additional terms that apply to those files, or a notice indicating | |
| 389 | + | where to find the applicable terms. | |
| 390 | + | ||
| 391 | + | Additional terms, permissive or non-permissive, may be stated in the | |
| 392 | + | form of a separately written license, or stated as exceptions; | |
| 393 | + | the above requirements apply either way. | |
| 394 | + | ||
| 395 | + | 8. Termination. | |
| 396 | + | ||
| 397 | + | You may not propagate or modify a covered work except as expressly | |
| 398 | + | provided under this License. Any attempt otherwise to propagate or | |
| 399 | + | modify it is void, and will automatically terminate your rights under | |
| 400 | + | this License (including any patent licenses granted under the third | |
| 401 | + | paragraph of section 11). | |
| 402 | + | ||
| 403 | + | However, if you cease all violation of this License, then your | |
| 404 | + | license from a particular copyright holder is reinstated (a) | |
| 405 | + | provisionally, unless and until the copyright holder explicitly and | |
| 406 | + | finally terminates your license, and (b) permanently, if the copyright | |
| 407 | + | holder fails to notify you of the violation by some reasonable means | |
| 408 | + | prior to 60 days after the cessation. | |
| 409 | + | ||
| 410 | + | Moreover, your license from a particular copyright holder is | |
| 411 | + | reinstated permanently if the copyright holder notifies you of the | |
| 412 | + | violation by some reasonable means, this is the first time you have | |
| 413 | + | received notice of violation of this License (for any work) from that | |
| 414 | + | copyright holder, and you cure the violation prior to 30 days after | |
| 415 | + | your receipt of the notice. | |
| 416 | + | ||
| 417 | + | Termination of your rights under this section does not terminate the | |
| 418 | + | licenses of parties who have received copies or rights from you under | |
| 419 | + | this License. If your rights have been terminated and not permanently | |
| 420 | + | reinstated, you do not qualify to receive new licenses for the same | |
| 421 | + | material under section 10. | |
| 422 | + | ||
| 423 | + | 9. Acceptance Not Required for Having Copies. | |
| 424 | + | ||
| 425 | + | You are not required to accept this License in order to receive or | |
| 426 | + | run a copy of the Program. Ancillary propagation of a covered work | |
| 427 | + | occurring solely as a consequence of using peer-to-peer transmission | |
| 428 | + | to receive a copy likewise does not require acceptance. However, | |
| 429 | + | nothing other than this License grants you permission to propagate or | |
| 430 | + | modify any covered work. These actions infringe copyright if you do | |
| 431 | + | not accept this License. Therefore, by modifying or propagating a | |
| 432 | + | covered work, you indicate your acceptance of this License to do so. | |
| 433 | + | ||
| 434 | + | 10. Automatic Licensing of Downstream Recipients. | |
| 435 | + | ||
| 436 | + | Each time you convey a covered work, the recipient automatically | |
| 437 | + | receives a license from the original licensors, to run, modify and | |
| 438 | + | propagate that work, subject to this License. You are not responsible | |
| 439 | + | for enforcing compliance by third parties with this License. | |
| 440 | + | ||
| 441 | + | An "entity transaction" is a transaction transferring control of an | |
| 442 | + | organization, or substantially all assets of one, or subdividing an | |
| 443 | + | organization, or merging organizations. If propagation of a covered | |
| 444 | + | work results from an entity transaction, each party to that | |
| 445 | + | transaction who receives a copy of the work also receives whatever | |
| 446 | + | licenses to the work the party's predecessor in interest had or could | |
| 447 | + | give under the previous paragraph, plus a right to possession of the | |
| 448 | + | Corresponding Source of the work from the predecessor in interest, if | |
| 449 | + | the predecessor has it or can get it with reasonable efforts. | |
| 450 | + | ||
| 451 | + | You may not impose any further restrictions on the exercise of the | |
| 452 | + | rights granted or affirmed under this License. For example, you may | |
| 453 | + | not impose a license fee, royalty, or other charge for exercise of | |
| 454 | + | rights granted under this License, and you may not initiate litigation | |
| 455 | + | (including a cross-claim or counterclaim in a lawsuit) alleging that | |
| 456 | + | any patent claim is infringed by making, using, selling, offering for | |
| 457 | + | sale, or importing the Program or any portion of it. | |
| 458 | + | ||
| 459 | + | 11. Patents. | |
| 460 | + | ||
| 461 | + | A "contributor" is a copyright holder who authorizes use under this | |
| 462 | + | License of the Program or a work on which the Program is based. The | |
| 463 | + | work thus licensed is called the contributor's "contributor version". | |
| 464 | + | ||
| 465 | + | A contributor's "essential patent claims" are all patent claims | |
| 466 | + | owned or controlled by the contributor, whether already acquired or | |
| 467 | + | hereafter acquired, that would be infringed by some manner, permitted | |
| 468 | + | by this License, of making, using, or selling its contributor version, | |
| 469 | + | but do not include claims that would be infringed only as a | |
| 470 | + | consequence of further modification of the contributor version. For | |
| 471 | + | purposes of this definition, "control" includes the right to grant | |
| 472 | + | patent sublicenses in a manner consistent with the requirements of | |
| 473 | + | this License. | |
| 474 | + | ||
| 475 | + | Each contributor grants you a non-exclusive, worldwide, royalty-free | |
| 476 | + | patent license under the contributor's essential patent claims, to | |
| 477 | + | make, use, sell, offer for sale, import and otherwise run, modify and | |
| 478 | + | propagate the contents of its contributor version. | |
| 479 | + | ||
| 480 | + | In the following three paragraphs, a "patent license" is any express | |
| 481 | + | agreement or commitment, however denominated, not to enforce a patent | |
| 482 | + | (such as an express permission to practice a patent or covenant not to | |
| 483 | + | sue for patent infringement). To "grant" such a patent license to a | |
| 484 | + | party means to make such an agreement or commitment not to enforce a | |
| 485 | + | patent against the party. | |
| 486 | + | ||
| 487 | + | If you convey a covered work, knowingly relying on a patent license, | |
| 488 | + | and the Corresponding Source of the work is not available for anyone | |
| 489 | + | to copy, free of charge and under the terms of this License, through a | |
| 490 | + | publicly available network server or other readily accessible means, | |
| 491 | + | then you must either (1) cause the Corresponding Source to be so | |
| 492 | + | available, or (2) arrange to deprive yourself of the benefit of the | |
| 493 | + | patent license for this particular work, or (3) arrange, in a manner | |
| 494 | + | consistent with the requirements of this License, to extend the patent | |
| 495 | + | license to downstream recipients. "Knowingly relying" means you have | |
| 496 | + | actual knowledge that, but for the patent license, your conveying the | |
| 497 | + | covered work in a country, or your recipient's use of the covered work | |
| 498 | + | in a country, would infringe one or more identifiable patents in that | |
| 499 | + | country that you have reason to believe are valid. | |
| 500 | + | ||
| 501 | + | If, pursuant to or in connection with a single transaction or | |
| 502 | + | arrangement, you convey, or propagate by procuring conveyance of, a | |
| 503 | + | covered work, and grant a patent license to some of the parties | |
| 504 | + | receiving the covered work authorizing them to use, propagate, modify | |
| 505 | + | or convey a specific copy of the covered work, then the patent license | |
| 506 | + | you grant is automatically extended to all recipients of the covered | |
| 507 | + | work and works based on it. | |
| 508 | + | ||
| 509 | + | A patent license is "discriminatory" if it does not include within | |
| 510 | + | the scope of its coverage, prohibits the exercise of, or is | |
| 511 | + | conditioned on the non-exercise of one or more of the rights that are | |
| 512 | + | specifically granted under this License. You may not convey a covered | |
| 513 | + | work if you are a party to an arrangement with a third party that is | |
| 514 | + | in the business of distributing software, under which you make payment | |
| 515 | + | to the third party based on the extent of your activity of conveying | |
| 516 | + | the work, and under which the third party grants, to any of the | |
| 517 | + | parties who would receive the covered work from you, a discriminatory | |
| 518 | + | patent license (a) in connection with copies of the covered work | |
| 519 | + | conveyed by you (or copies made from those copies), or (b) primarily | |
| 520 | + | for and in connection with specific products or compilations that | |
| 521 | + | contain the covered work, unless you entered into that arrangement, | |
| 522 | + | or that patent license was granted, prior to 28 March 2007. | |
| 523 | + | ||
| 524 | + | Nothing in this License shall be construed as excluding or limiting | |
| 525 | + | any implied license or other defenses to infringement that may | |
| 526 | + | otherwise be available to you under applicable patent law. | |
| 527 | + | ||
| 528 | + | 12. No Surrender of Others' Freedom. | |
| 529 | + | ||
| 530 | + | If conditions are imposed on you (whether by court order, agreement or | |
| 531 | + | otherwise) that contradict the conditions of this License, they do not | |
| 532 | + | excuse you from the conditions of this License. If you cannot convey a | |
| 533 | + | covered work so as to satisfy simultaneously your obligations under this | |
| 534 | + | License and any other pertinent obligations, then as a consequence you may | |
| 535 | + | not convey it at all. For example, if you agree to terms that obligate you | |
| 536 | + | to collect a royalty for further conveying from those to whom you convey | |
| 537 | + | the Program, the only way you could satisfy both those terms and this | |
| 538 | + | License would be to refrain entirely from conveying the Program. | |
| 539 | + | ||
| 540 | + | 13. Remote Network Interaction; Use with the GNU General Public License. | |
| 541 | + | ||
| 542 | + | Notwithstanding any other provision of this License, if you modify the | |
| 543 | + | Program, your modified version must prominently offer all users | |
| 544 | + | interacting with it remotely through a computer network (if your version | |
| 545 | + | supports such interaction) an opportunity to receive the Corresponding | |
| 546 | + | Source of your version by providing access to the Corresponding Source | |
| 547 | + | from a network server at no charge, through some standard or customary | |
| 548 | + | means of facilitating copying of software. This Corresponding Source | |
| 549 | + | shall include the Corresponding Source for any work covered by version 3 | |
| 550 | + | of the GNU General Public License that is incorporated pursuant to the | |
| 551 | + | following paragraph. | |
| 552 | + | ||
| 553 | + | Notwithstanding any other provision of this License, you have | |
| 554 | + | permission to link or combine any covered work with a work licensed | |
| 555 | + | under version 3 of the GNU General Public License into a single | |
| 556 | + | combined work, and to convey the resulting work. The terms of this | |
| 557 | + | License will continue to apply to the part which is the covered work, | |
| 558 | + | but the work with which it is combined will remain governed by version | |
| 559 | + | 3 of the GNU General Public License. | |
| 560 | + | ||
| 561 | + | 14. Revised Versions of this License. | |
| 562 | + | ||
| 563 | + | The Free Software Foundation may publish revised and/or new versions of | |
| 564 | + | the GNU Affero General Public License from time to time. Such new versions | |
| 565 | + | will be similar in spirit to the present version, but may differ in detail to | |
| 566 | + | address new problems or concerns. | |
| 567 | + | ||
| 568 | + | Each version is given a distinguishing version number. If the | |
| 569 | + | Program specifies that a certain numbered version of the GNU Affero General | |
| 570 | + | Public License "or any later version" applies to it, you have the | |
| 571 | + | option of following the terms and conditions either of that numbered | |
| 572 | + | version or of any later version published by the Free Software | |
| 573 | + | Foundation. If the Program does not specify a version number of the | |
| 574 | + | GNU Affero General Public License, you may choose any version ever published | |
| 575 | + | by the Free Software Foundation. | |
| 576 | + | ||
| 577 | + | If the Program specifies that a proxy can decide which future | |
| 578 | + | versions of the GNU Affero General Public License can be used, that proxy's | |
| 579 | + | public statement of acceptance of a version permanently authorizes you | |
| 580 | + | to choose that version for the Program. | |
| 581 | + | ||
| 582 | + | Later license versions may give you additional or different | |
| 583 | + | permissions. However, no additional obligations are imposed on any | |
| 584 | + | author or copyright holder as a result of your choosing to follow a | |
| 585 | + | later version. | |
| 586 | + | ||
| 587 | + | 15. Disclaimer of Warranty. | |
| 588 | + | ||
| 589 | + | THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY | |
| 590 | + | APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT | |
| 591 | + | HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY | |
| 592 | + | OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, | |
| 593 | + | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR | |
| 594 | + | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM | |
| 595 | + | IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF | |
| 596 | + | ALL NECESSARY SERVICING, REPAIR OR CORRECTION. | |
| 597 | + | ||
| 598 | + | 16. Limitation of Liability. | |
| 599 | + | ||
| 600 | + | IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING | |
| 601 | + | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS | |
| 602 | + | THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY | |
| 603 | + | GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE | |
| 604 | + | USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF | |
| 605 | + | DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD | |
| 606 | + | PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), | |
| 607 | + | EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF | |
| 608 | + | SUCH DAMAGES. | |
| 609 | + | ||
| 610 | + | 17. Interpretation of Sections 15 and 16. | |
| 611 | + | ||
| 612 | + | If the disclaimer of warranty and limitation of liability provided | |
| 613 | + | above cannot be given local legal effect according to their terms, | |
| 614 | + | reviewing courts shall apply local law that most closely approximates | |
| 615 | + | an absolute waiver of all civil liability in connection with the | |
| 616 | + | Program, unless a warranty or assumption of liability accompanies a | |
| 617 | + | copy of the Program in return for a fee. | |
| 618 | + | ||
| 619 | + | END OF TERMS AND CONDITIONS | |
| 620 | + | ||
| 621 | + | How to Apply These Terms to Your New Programs | |
| 622 | + | ||
| 623 | + | If you develop a new program, and you want it to be of the greatest | |
| 624 | + | possible use to the public, the best way to achieve this is to make it | |
| 625 | + | free software which everyone can redistribute and change under these terms. | |
| 626 | + | ||
| 627 | + | To do so, attach the following notices to the program. It is safest | |
| 628 | + | to attach them to the start of each source file to most effectively | |
| 629 | + | state the exclusion of warranty; and each file should have at least | |
| 630 | + | the "copyright" line and a pointer to where the full notice is found. | |
| 631 | + | ||
| 632 | + | <one line to give the program's name and a brief idea of what it does.> | |
| 633 | + | Copyright (C) <year> <name of author> | |
| 634 | + | ||
| 635 | + | This program is free software: you can redistribute it and/or modify | |
| 636 | + | it under the terms of the GNU Affero General Public License as published | |
| 637 | + | by the Free Software Foundation, either version 3 of the License, or | |
| 638 | + | (at your option) any later version. | |
| 639 | + | ||
| 640 | + | This program is distributed in the hope that it will be useful, | |
| 641 | + | but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| 642 | + | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
| 643 | + | GNU Affero General Public License for more details. | |
| 644 | + | ||
| 645 | + | You should have received a copy of the GNU Affero General Public License | |
| 646 | + | along with this program. If not, see <https://www.gnu.org/licenses/>. | |
| 647 | + | ||
| 648 | + | Also add information on how to contact you by electronic and paper mail. | |
| 649 | + | ||
| 650 | + | If your software can interact with users remotely through a computer | |
| 651 | + | network, you should also make sure that it provides a way for users to | |
| 652 | + | get its source. For example, if your program is a web application, its | |
| 653 | + | interface could display a "Source" link that leads users to an archive | |
| 654 | + | of the code. There are many ways you could offer source, and different | |
| 655 | + | solutions will be better for different programs; see section 13 for the | |
| 656 | + | specific requirements. | |
| 657 | + | ||
| 658 | + | You should also get your employer (if you work as a programmer) or school, | |
| 659 | + | if any, to sign a "copyright disclaimer" for the program, if necessary. | |
| 660 | + | For more information on this, and how to apply and follow the GNU AGPL, see | |
| 661 | + | <https://www.gnu.org/licenses/>. | |
Aassets/default.css
| @@ -0,0 +1,75 @@ | |||
|---|---|---|---|
| 1 | + | @import "normalize.css"; | |
| 2 | + | @import "sakura-vader.css"; | |
| 3 | + | ||
| 4 | + | #jsguard { | |
| 5 | + | display: none; | |
| 6 | + | } | |
| 7 | + | ||
| 8 | + | body { | |
| 9 | + | display: flex; | |
| 10 | + | align-items: center; | |
| 11 | + | flex-direction: column; | |
| 12 | + | min-height: 100vh; | |
| 13 | + | ||
| 14 | + | padding: 3rem; | |
| 15 | + | box-sizing: border-box; | |
| 16 | + | max-width: 80rem; | |
| 17 | + | } | |
| 18 | + | ||
| 19 | + | a { | |
| 20 | + | text-wrap: nowrap; | |
| 21 | + | } | |
| 22 | + | ||
| 23 | + | hr { | |
| 24 | + | width: 100%; | |
| 25 | + | } | |
| 26 | + | ||
| 27 | + | h1 { | |
| 28 | + | margin-bottom: 4rem; | |
| 29 | + | } | |
| 30 | + | ||
| 31 | + | form { | |
| 32 | + | display: flex; | |
| 33 | + | flex-direction: column; | |
| 34 | + | gap: 0.5rem; | |
| 35 | + | margin-bottom: 1.5rem; | |
| 36 | + | min-width: 35rem; | |
| 37 | + | width: 100%; | |
| 38 | + | ||
| 39 | + | input { | |
| 40 | + | flex-grow: 1; | |
| 41 | + | } | |
| 42 | + | ||
| 43 | + | input[type=checkbox] { | |
| 44 | + | flex-grow: 0; | |
| 45 | + | margin-bottom: 0.5rem; | |
| 46 | + | } | |
| 47 | + | ||
| 48 | + | div { | |
| 49 | + | display: flex; | |
| 50 | + | flex-direction: row; | |
| 51 | + | align-items: center; | |
| 52 | + | gap: 1rem; | |
| 53 | + | } | |
| 54 | + | } | |
| 55 | + | ||
| 56 | + | select { | |
| 57 | + | overflow: auto; | |
| 58 | + | flex-grow: 1; | |
| 59 | + | } | |
| 60 | + | ||
| 61 | + | img { | |
| 62 | + | margin-bottom: 0; | |
| 63 | + | } | |
| 64 | + | ||
| 65 | + | .help-icon { | |
| 66 | + | width: 2rem; | |
| 67 | + | height: 2rem; | |
| 68 | + | filter: invert(); | |
| 69 | + | margin-left: 0.5rem; | |
| 70 | + | } | |
| 71 | + | ||
| 72 | + | input[type=reset] { | |
| 73 | + | max-width: 25rem; | |
| 74 | + | align-self: center; | |
| 75 | + | } | |
Aassets/encrypt.py
| @@ -0,0 +1,54 @@ | |||
|---|---|---|---|
| 1 | + | from cryptography.hazmat.primitives import hashes | |
| 2 | + | from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC | |
| 3 | + | from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes | |
| 4 | + | from cryptography.hazmat.primitives import padding | |
| 5 | + | from cryptography.hazmat.backends import default_backend | |
| 6 | + | import os | |
| 7 | + | import sys | |
| 8 | + | ||
| 9 | + | def encrypt(content: bytes, password: str) -> bytes: | |
| 10 | + | """ | |
| 11 | + | Encrypts the given content using the provided password. | |
| 12 | + | ||
| 13 | + | Args: | |
| 14 | + | content (bytes): The content to be encrypted. | |
| 15 | + | password (str): The password used for encryption. | |
| 16 | + | ||
| 17 | + | Returns: | |
| 18 | + | bytes: The encrypted content. | |
| 19 | + | """ | |
| 20 | + | ||
| 21 | + | # Generate a random initialization vector (IV) and salt | |
| 22 | + | iv = os.urandom(16) | |
| 23 | + | salt = os.urandom(16) | |
| 24 | + | ||
| 25 | + | # Derive a key from the password using PBKDF2 | |
| 26 | + | kdf = PBKDF2HMAC( | |
| 27 | + | algorithm=hashes.SHA512(), | |
| 28 | + | length=32, | |
| 29 | + | salt=salt, | |
| 30 | + | iterations=100000, | |
| 31 | + | backend=default_backend() | |
| 32 | + | ) | |
| 33 | + | key = kdf.derive(password.encode()) | |
| 34 | + | ||
| 35 | + | # Create a cipher object with AES-256-CBC | |
| 36 | + | cipher = Cipher(algorithms.AES(key), modes.CBC(iv), backend=default_backend()) | |
| 37 | + | encryptor = cipher.encryptor() | |
| 38 | + | ||
| 39 | + | # Pad the content to a multiple of the block length | |
| 40 | + | padder = padding.PKCS7(128).padder() | |
| 41 | + | padded_content = padder.update(content) + padder.finalize() | |
| 42 | + | ||
| 43 | + | # Encrypt the padded content | |
| 44 | + | encrypted_content = encryptor.update(padded_content) + encryptor.finalize() | |
| 45 | + | ||
| 46 | + | # Return the salt, IV, and encrypted content concatenated | |
| 47 | + | return salt + iv + encrypted_content | |
| 48 | + | if __name__ == "__main__": | |
| 49 | + | if len(sys.argv) != 4: | |
| 50 | + | print("Usage: encrypt.py <file> <password> <outfile>") | |
| 51 | + | sys.exit(1) | |
| 52 | + | data = encrypt(open(sys.argv[1], "rb").read(), sys.argv[2]) | |
| 53 | + | open(sys.argv[3], "wb").write(data) | |
| 54 | + | ||
Aassets/highlight.css
| @@ -0,0 +1 @@ | |||
|---|---|---|---|
| 1 | + | pre code.hljs{display:block;overflow-x:auto;padding:1em}code.hljs{padding:3px 5px}.hljs{background:#1d1f21;color:#c5c8c6}.hljs span::selection,.hljs::selection{background:#373b41}.hljs span::-moz-selection,.hljs::-moz-selection{background:#373b41}.hljs-name,.hljs-title{color:#f0c674}.hljs-comment,.hljs-meta,.hljs-meta .hljs-keyword{color:#707880}.hljs-deletion,.hljs-link,.hljs-literal,.hljs-number,.hljs-symbol{color:#c66}.hljs-addition,.hljs-doctag,.hljs-regexp,.hljs-selector-attr,.hljs-selector-pseudo,.hljs-string{color:#b5bd68}.hljs-attribute,.hljs-code,.hljs-selector-id{color:#b294bb}.hljs-bullet,.hljs-keyword,.hljs-selector-tag,.hljs-tag{color:#81a2be}.hljs-subst,.hljs-template-tag,.hljs-template-variable,.hljs-variable{color:#8abeb7}.hljs-built_in,.hljs-quote,.hljs-section,.hljs-selector-class,.hljs-type{color:#de935f}.hljs-emphasis{font-style:italic}.hljs-strong{font-weight:700} | |
Aassets/normalize.css
| @@ -0,0 +1,461 @@ | |||
|---|---|---|---|
| 1 | + | /*! normalize.css v5.0.0 | MIT License | github.com/necolas/normalize.css */ | |
| 2 | + | ||
| 3 | + | /** | |
| 4 | + | * 1. Change the default font family in all browsers (opinionated). | |
| 5 | + | * 2. Correct the line height in all browsers. | |
| 6 | + | * 3. Prevent adjustments of font size after orientation changes in | |
| 7 | + | * IE on Windows Phone and in iOS. | |
| 8 | + | */ | |
| 9 | + | ||
| 10 | + | /* Document | |
| 11 | + | ========================================================================== */ | |
| 12 | + | ||
| 13 | + | html { | |
| 14 | + | font-family: sans-serif; /* 1 */ | |
| 15 | + | line-height: 1.15; /* 2 */ | |
| 16 | + | -ms-text-size-adjust: 100%; /* 3 */ | |
| 17 | + | -webkit-text-size-adjust: 100%; /* 3 */ | |
| 18 | + | } | |
| 19 | + | ||
| 20 | + | /* Sections | |
| 21 | + | ========================================================================== */ | |
| 22 | + | ||
| 23 | + | /** | |
| 24 | + | * Remove the margin in all browsers (opinionated). | |
| 25 | + | */ | |
| 26 | + | ||
| 27 | + | body { | |
| 28 | + | margin: 0; | |
| 29 | + | } | |
| 30 | + | ||
| 31 | + | /** | |
| 32 | + | * Add the correct display in IE 9-. | |
| 33 | + | */ | |
| 34 | + | ||
| 35 | + | article, | |
| 36 | + | aside, | |
| 37 | + | footer, | |
| 38 | + | header, | |
| 39 | + | nav, | |
| 40 | + | section { | |
| 41 | + | display: block; | |
| 42 | + | } | |
| 43 | + | ||
| 44 | + | /** | |
| 45 | + | * Correct the font size and margin on `h1` elements within `section` and | |
| 46 | + | * `article` contexts in Chrome, Firefox, and Safari. | |
| 47 | + | */ | |
| 48 | + | ||
| 49 | + | h1 { | |
| 50 | + | font-size: 2em; | |
| 51 | + | margin: 0.67em 0; | |
| 52 | + | } | |
| 53 | + | ||
| 54 | + | /* Grouping content | |
| 55 | + | ========================================================================== */ | |
| 56 | + | ||
| 57 | + | /** | |
| 58 | + | * Add the correct display in IE 9-. | |
| 59 | + | * 1. Add the correct display in IE. | |
| 60 | + | */ | |
| 61 | + | ||
| 62 | + | figcaption, | |
| 63 | + | figure, | |
| 64 | + | main { /* 1 */ | |
| 65 | + | display: block; | |
| 66 | + | } | |
| 67 | + | ||
| 68 | + | /** | |
| 69 | + | * Add the correct margin in IE 8. | |
| 70 | + | */ | |
| 71 | + | ||
| 72 | + | figure { | |
| 73 | + | margin: 1em 40px; | |
| 74 | + | } | |
| 75 | + | ||
| 76 | + | /** | |
| 77 | + | * 1. Add the correct box sizing in Firefox. | |
| 78 | + | * 2. Show the overflow in Edge and IE. | |
| 79 | + | */ | |
| 80 | + | ||
| 81 | + | hr { | |
| 82 | + | box-sizing: content-box; /* 1 */ | |
| 83 | + | height: 0; /* 1 */ | |
| 84 | + | overflow: visible; /* 2 */ | |
| 85 | + | } | |
| 86 | + | ||
| 87 | + | /** | |
| 88 | + | * 1. Correct the inheritance and scaling of font size in all browsers. | |
| 89 | + | * 2. Correct the odd `em` font sizing in all browsers. | |
| 90 | + | */ | |
| 91 | + | ||
| 92 | + | pre { | |
| 93 | + | font-family: monospace, monospace; /* 1 */ | |
| 94 | + | font-size: 1em; /* 2 */ | |
| 95 | + | } | |
| 96 | + | ||
| 97 | + | /* Text-level semantics | |
| 98 | + | ========================================================================== */ | |
| 99 | + | ||
| 100 | + | /** | |
| 101 | + | * 1. Remove the gray background on active links in IE 10. | |
| 102 | + | * 2. Remove gaps in links underline in iOS 8+ and Safari 8+. | |
| 103 | + | */ | |
| 104 | + | ||
| 105 | + | a { | |
| 106 | + | background-color: transparent; /* 1 */ | |
| 107 | + | -webkit-text-decoration-skip: objects; /* 2 */ | |
| 108 | + | } | |
| 109 | + | ||
| 110 | + | /** | |
| 111 | + | * Remove the outline on focused links when they are also active or hovered | |
| 112 | + | * in all browsers (opinionated). | |
| 113 | + | */ | |
| 114 | + | ||
| 115 | + | a:active, | |
| 116 | + | a:hover { | |
| 117 | + | outline-width: 0; | |
| 118 | + | } | |
| 119 | + | ||
| 120 | + | /** | |
| 121 | + | * 1. Remove the bottom border in Firefox 39-. | |
| 122 | + | * 2. Add the correct text decoration in Chrome, Edge, IE, Opera, and Safari. | |
| 123 | + | */ | |
| 124 | + | ||
| 125 | + | abbr[title] { | |
| 126 | + | border-bottom: none; /* 1 */ | |
| 127 | + | text-decoration: underline; /* 2 */ | |
| 128 | + | text-decoration: underline dotted; /* 2 */ | |
| 129 | + | } | |
| 130 | + | ||
| 131 | + | /** | |
| 132 | + | * Prevent the duplicate application of `bolder` by the next rule in Safari 6. | |
| 133 | + | */ | |
| 134 | + | ||
| 135 | + | b, | |
| 136 | + | strong { | |
| 137 | + | font-weight: inherit; | |
| 138 | + | } | |
| 139 | + | ||
| 140 | + | /** | |
| 141 | + | * Add the correct font weight in Chrome, Edge, and Safari. | |
| 142 | + | */ | |
| 143 | + | ||
| 144 | + | b, | |
| 145 | + | strong { | |
| 146 | + | font-weight: bolder; | |
| 147 | + | } | |
| 148 | + | ||
| 149 | + | /** | |
| 150 | + | * 1. Correct the inheritance and scaling of font size in all browsers. | |
| 151 | + | * 2. Correct the odd `em` font sizing in all browsers. | |
| 152 | + | */ | |
| 153 | + | ||
| 154 | + | code, | |
| 155 | + | kbd, | |
| 156 | + | samp { | |
| 157 | + | font-family: monospace, monospace; /* 1 */ | |
| 158 | + | font-size: 1em; /* 2 */ | |
| 159 | + | } | |
| 160 | + | ||
| 161 | + | /** | |
| 162 | + | * Add the correct font style in Android 4.3-. | |
| 163 | + | */ | |
| 164 | + | ||
| 165 | + | dfn { | |
| 166 | + | font-style: italic; | |
| 167 | + | } | |
| 168 | + | ||
| 169 | + | /** | |
| 170 | + | * Add the correct background and color in IE 9-. | |
| 171 | + | */ | |
| 172 | + | ||
| 173 | + | mark { | |
| 174 | + | background-color: #ff0; | |
| 175 | + | color: #000; | |
| 176 | + | } | |
| 177 | + | ||
| 178 | + | /** | |
| 179 | + | * Add the correct font size in all browsers. | |
| 180 | + | */ | |
| 181 | + | ||
| 182 | + | small { | |
| 183 | + | font-size: 80%; | |
| 184 | + | } | |
| 185 | + | ||
| 186 | + | /** | |
| 187 | + | * Prevent `sub` and `sup` elements from affecting the line height in | |
| 188 | + | * all browsers. | |
| 189 | + | */ | |
| 190 | + | ||
| 191 | + | sub, | |
| 192 | + | sup { | |
| 193 | + | font-size: 75%; | |
| 194 | + | line-height: 0; | |
| 195 | + | position: relative; | |
| 196 | + | vertical-align: baseline; | |
| 197 | + | } | |
| 198 | + | ||
| 199 | + | sub { | |
| 200 | + | bottom: -0.25em; | |
| 201 | + | } | |
| 202 | + | ||
| 203 | + | sup { | |
| 204 | + | top: -0.5em; | |
| 205 | + | } | |
| 206 | + | ||
| 207 | + | /* Embedded content | |
| 208 | + | ========================================================================== */ | |
| 209 | + | ||
| 210 | + | /** | |
| 211 | + | * Add the correct display in IE 9-. | |
| 212 | + | */ | |
| 213 | + | ||
| 214 | + | audio, | |
| 215 | + | video { | |
| 216 | + | display: inline-block; | |
| 217 | + | } | |
| 218 | + | ||
| 219 | + | /** | |
| 220 | + | * Add the correct display in iOS 4-7. | |
| 221 | + | */ | |
| 222 | + | ||
| 223 | + | audio:not([controls]) { | |
| 224 | + | display: none; | |
| 225 | + | height: 0; | |
| 226 | + | } | |
| 227 | + | ||
| 228 | + | /** | |
| 229 | + | * Remove the border on images inside links in IE 10-. | |
| 230 | + | */ | |
| 231 | + | ||
| 232 | + | img { | |
| 233 | + | border-style: none; | |
| 234 | + | } | |
| 235 | + | ||
| 236 | + | /** | |
| 237 | + | * Hide the overflow in IE. | |
| 238 | + | */ | |
| 239 | + | ||
| 240 | + | svg:not(:root) { | |
| 241 | + | overflow: hidden; | |
| 242 | + | } | |
| 243 | + | ||
| 244 | + | /* Forms | |
| 245 | + | ========================================================================== */ | |
| 246 | + | ||
| 247 | + | /** | |
| 248 | + | * 1. Change the font styles in all browsers (opinionated). | |
| 249 | + | * 2. Remove the margin in Firefox and Safari. | |
| 250 | + | */ | |
| 251 | + | ||
| 252 | + | button, | |
| 253 | + | input, | |
| 254 | + | optgroup, | |
| 255 | + | select, | |
| 256 | + | textarea { | |
| 257 | + | font-family: sans-serif; /* 1 */ | |
| 258 | + | font-size: 100%; /* 1 */ | |
| 259 | + | line-height: 1.15; /* 1 */ | |
| 260 | + | margin: 0; /* 2 */ | |
| 261 | + | } | |
| 262 | + | ||
| 263 | + | /** | |
| 264 | + | * Show the overflow in IE. | |
| 265 | + | * 1. Show the overflow in Edge. | |
| 266 | + | */ | |
| 267 | + | ||
| 268 | + | button, | |
| 269 | + | input { /* 1 */ | |
| 270 | + | overflow: visible; | |
| 271 | + | } | |
| 272 | + | ||
| 273 | + | /** | |
| 274 | + | * Remove the inheritance of text transform in Edge, Firefox, and IE. | |
| 275 | + | * 1. Remove the inheritance of text transform in Firefox. | |
| 276 | + | */ | |
| 277 | + | ||
| 278 | + | button, | |
| 279 | + | select { /* 1 */ | |
| 280 | + | text-transform: none; | |
| 281 | + | } | |
| 282 | + | ||
| 283 | + | /** | |
| 284 | + | * 1. Prevent a WebKit bug where (2) destroys native `audio` and `video` | |
| 285 | + | * controls in Android 4. | |
| 286 | + | * 2. Correct the inability to style clickable types in iOS and Safari. | |
| 287 | + | */ | |
| 288 | + | ||
| 289 | + | button, | |
| 290 | + | html [type="button"], /* 1 */ | |
| 291 | + | [type="reset"], | |
| 292 | + | [type="submit"] { | |
| 293 | + | -webkit-appearance: button; /* 2 */ | |
| 294 | + | } | |
| 295 | + | ||
| 296 | + | /** | |
| 297 | + | * Remove the inner border and padding in Firefox. | |
| 298 | + | */ | |
| 299 | + | ||
| 300 | + | button::-moz-focus-inner, | |
| 301 | + | [type="button"]::-moz-focus-inner, | |
| 302 | + | [type="reset"]::-moz-focus-inner, | |
| 303 | + | [type="submit"]::-moz-focus-inner { | |
| 304 | + | border-style: none; | |
| 305 | + | padding: 0; | |
| 306 | + | } | |
| 307 | + | ||
| 308 | + | /** | |
| 309 | + | * Restore the focus styles unset by the previous rule. | |
| 310 | + | */ | |
| 311 | + | ||
| 312 | + | button:-moz-focusring, | |
| 313 | + | [type="button"]:-moz-focusring, | |
| 314 | + | [type="reset"]:-moz-focusring, | |
| 315 | + | [type="submit"]:-moz-focusring { | |
| 316 | + | outline: 1px dotted ButtonText; | |
| 317 | + | } | |
| 318 | + | ||
| 319 | + | /** | |
| 320 | + | * Change the border, margin, and padding in all browsers (opinionated). | |
| 321 | + | */ | |
| 322 | + | ||
| 323 | + | fieldset { | |
| 324 | + | border: 1px solid #c0c0c0; | |
| 325 | + | margin: 0 2px; | |
| 326 | + | padding: 0.35em 0.625em 0.75em; | |
| 327 | + | } | |
| 328 | + | ||
| 329 | + | /** | |
| 330 | + | * 1. Correct the text wrapping in Edge and IE. | |
| 331 | + | * 2. Correct the color inheritance from `fieldset` elements in IE. | |
| 332 | + | * 3. Remove the padding so developers are not caught out when they zero out | |
| 333 | + | * `fieldset` elements in all browsers. | |
| 334 | + | */ | |
| 335 | + | ||
| 336 | + | legend { | |
| 337 | + | box-sizing: border-box; /* 1 */ | |
| 338 | + | color: inherit; /* 2 */ | |
| 339 | + | display: table; /* 1 */ | |
| 340 | + | max-width: 100%; /* 1 */ | |
| 341 | + | padding: 0; /* 3 */ | |
| 342 | + | white-space: normal; /* 1 */ | |
| 343 | + | } | |
| 344 | + | ||
| 345 | + | /** | |
| 346 | + | * 1. Add the correct display in IE 9-. | |
| 347 | + | * 2. Add the correct vertical alignment in Chrome, Firefox, and Opera. | |
| 348 | + | */ | |
| 349 | + | ||
| 350 | + | progress { | |
| 351 | + | display: inline-block; /* 1 */ | |
| 352 | + | vertical-align: baseline; /* 2 */ | |
| 353 | + | } | |
| 354 | + | ||
| 355 | + | /** | |
| 356 | + | * Remove the default vertical scrollbar in IE. | |
| 357 | + | */ | |
| 358 | + | ||
| 359 | + | textarea { | |
| 360 | + | overflow: auto; | |
| 361 | + | } | |
| 362 | + | ||
| 363 | + | /** | |
| 364 | + | * 1. Add the correct box sizing in IE 10-. | |
| 365 | + | * 2. Remove the padding in IE 10-. | |
| 366 | + | */ | |
| 367 | + | ||
| 368 | + | [type="checkbox"], | |
| 369 | + | [type="radio"] { | |
| 370 | + | box-sizing: border-box; /* 1 */ | |
| 371 | + | padding: 0; /* 2 */ | |
| 372 | + | } | |
| 373 | + | ||
| 374 | + | /** | |
| 375 | + | * Correct the cursor style of increment and decrement buttons in Chrome. | |
| 376 | + | */ | |
| 377 | + | ||
| 378 | + | [type="number"]::-webkit-inner-spin-button, | |
| 379 | + | [type="number"]::-webkit-outer-spin-button { | |
| 380 | + | height: auto; | |
| 381 | + | } | |
| 382 | + | ||
| 383 | + | /** | |
| 384 | + | * 1. Correct the odd appearance in Chrome and Safari. | |
| 385 | + | * 2. Correct the outline style in Safari. | |
| 386 | + | */ | |
| 387 | + | ||
| 388 | + | [type="search"] { | |
| 389 | + | -webkit-appearance: textfield; /* 1 */ | |
| 390 | + | outline-offset: -2px; /* 2 */ | |
| 391 | + | } | |
| 392 | + | ||
| 393 | + | /** | |
| 394 | + | * Remove the inner padding and cancel buttons in Chrome and Safari on macOS. | |
| 395 | + | */ | |
| 396 | + | ||
| 397 | + | [type="search"]::-webkit-search-cancel-button, | |
| 398 | + | [type="search"]::-webkit-search-decoration { | |
| 399 | + | -webkit-appearance: none; | |
| 400 | + | } | |
| 401 | + | ||
| 402 | + | /** | |
| 403 | + | * 1. Correct the inability to style clickable types in iOS and Safari. | |
| 404 | + | * 2. Change font properties to `inherit` in Safari. | |
| 405 | + | */ | |
| 406 | + | ||
| 407 | + | ::-webkit-file-upload-button { | |
| 408 | + | -webkit-appearance: button; /* 1 */ | |
| 409 | + | font: inherit; /* 2 */ | |
| 410 | + | } | |
| 411 | + | ||
| 412 | + | /* Interactive | |
| 413 | + | ========================================================================== */ | |
| 414 | + | ||
| 415 | + | /* | |
| 416 | + | * Add the correct display in IE 9-. | |
| 417 | + | * 1. Add the correct display in Edge, IE, and Firefox. | |
| 418 | + | */ | |
| 419 | + | ||
| 420 | + | details, /* 1 */ | |
| 421 | + | menu { | |
| 422 | + | display: block; | |
| 423 | + | } | |
| 424 | + | ||
| 425 | + | /* | |
| 426 | + | * Add the correct display in all browsers. | |
| 427 | + | */ | |
| 428 | + | ||
| 429 | + | summary { | |
| 430 | + | display: list-item; | |
| 431 | + | } | |
| 432 | + | ||
| 433 | + | /* Scripting | |
| 434 | + | ========================================================================== */ | |
| 435 | + | ||
| 436 | + | /** | |
| 437 | + | * Add the correct display in IE 9-. | |
| 438 | + | */ | |
| 439 | + | ||
| 440 | + | canvas { | |
| 441 | + | display: inline-block; | |
| 442 | + | } | |
| 443 | + | ||
| 444 | + | /** | |
| 445 | + | * Add the correct display in IE. | |
| 446 | + | */ | |
| 447 | + | ||
| 448 | + | template { | |
| 449 | + | display: none; | |
| 450 | + | } | |
| 451 | + | ||
| 452 | + | /* Hidden | |
| 453 | + | ========================================================================== */ | |
| 454 | + | ||
| 455 | + | /** | |
| 456 | + | * Add the correct display in IE 10-. | |
| 457 | + | */ | |
| 458 | + | ||
| 459 | + | [hidden] { | |
| 460 | + | display: none; | |
| 461 | + | } | |
Aassets/sakura-vader.css
| @@ -0,0 +1,227 @@ | |||
|---|---|---|---|
| 1 | + | /* $color-text: #dedce5; */ | |
| 2 | + | /* Sakura.css v1.5.0 | |
| 3 | + | * ================ | |
| 4 | + | * Minimal css theme. | |
| 5 | + | * Project: https://github.com/oxalorg/sakura/ | |
| 6 | + | */ | |
| 7 | + | /* Body */ | |
| 8 | + | html { | |
| 9 | + | font-size: 62.5%; | |
| 10 | + | font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif; | |
| 11 | + | } | |
| 12 | + | ||
| 13 | + | body { | |
| 14 | + | font-size: 1.8rem; | |
| 15 | + | line-height: 1.618; | |
| 16 | + | max-width: 38em; | |
| 17 | + | margin: auto; | |
| 18 | + | color: #d9d8dc; | |
| 19 | + | background-color: #120c0e; | |
| 20 | + | padding: 13px; | |
| 21 | + | } | |
| 22 | + | ||
| 23 | + | @media (max-width: 684px) { | |
| 24 | + | body { | |
| 25 | + | font-size: 1.53rem; | |
| 26 | + | } | |
| 27 | + | } | |
| 28 | + | @media (max-width: 382px) { | |
| 29 | + | body { | |
| 30 | + | font-size: 1.35rem; | |
| 31 | + | } | |
| 32 | + | } | |
| 33 | + | h1, h2, h3, h4, h5, h6 { | |
| 34 | + | line-height: 1.1; | |
| 35 | + | font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif; | |
| 36 | + | font-weight: 700; | |
| 37 | + | margin-top: 3rem; | |
| 38 | + | margin-bottom: 1.5rem; | |
| 39 | + | overflow-wrap: break-word; | |
| 40 | + | word-wrap: break-word; | |
| 41 | + | -ms-word-break: break-all; | |
| 42 | + | word-break: break-word; | |
| 43 | + | } | |
| 44 | + | ||
| 45 | + | h1 { | |
| 46 | + | font-size: 2.35em; | |
| 47 | + | } | |
| 48 | + | ||
| 49 | + | h2 { | |
| 50 | + | font-size: 2em; | |
| 51 | + | } | |
| 52 | + | ||
| 53 | + | h3 { | |
| 54 | + | font-size: 1.75em; | |
| 55 | + | } | |
| 56 | + | ||
| 57 | + | h4 { | |
| 58 | + | font-size: 1.5em; | |
| 59 | + | } | |
| 60 | + | ||
| 61 | + | h5 { | |
| 62 | + | font-size: 1.25em; | |
| 63 | + | } | |
| 64 | + | ||
| 65 | + | h6 { | |
| 66 | + | font-size: 1em; | |
| 67 | + | } | |
| 68 | + | ||
| 69 | + | p { | |
| 70 | + | margin-top: 0px; | |
| 71 | + | margin-bottom: 2.5rem; | |
| 72 | + | } | |
| 73 | + | ||
| 74 | + | small, sub, sup { | |
| 75 | + | font-size: 75%; | |
| 76 | + | } | |
| 77 | + | ||
| 78 | + | hr { | |
| 79 | + | border-color: #eb99a1; | |
| 80 | + | } | |
| 81 | + | ||
| 82 | + | a { | |
| 83 | + | text-decoration: none; | |
| 84 | + | color: #eb99a1; | |
| 85 | + | } | |
| 86 | + | a:visited { | |
| 87 | + | color: #e26f7a; | |
| 88 | + | } | |
| 89 | + | a:hover { | |
| 90 | + | color: #DA4453; | |
| 91 | + | border-bottom: 2px solid #d9d8dc; | |
| 92 | + | } | |
| 93 | + | ||
| 94 | + | ul { | |
| 95 | + | padding-left: 1.4em; | |
| 96 | + | margin-top: 0px; | |
| 97 | + | margin-bottom: 2.5rem; | |
| 98 | + | } | |
| 99 | + | ||
| 100 | + | li { | |
| 101 | + | margin-bottom: 0.4em; | |
| 102 | + | } | |
| 103 | + | ||
| 104 | + | blockquote { | |
| 105 | + | margin-left: 0px; | |
| 106 | + | margin-right: 0px; | |
| 107 | + | padding-left: 1em; | |
| 108 | + | padding-top: 0.8em; | |
| 109 | + | padding-bottom: 0.8em; | |
| 110 | + | padding-right: 0.8em; | |
| 111 | + | border-left: 5px solid #eb99a1; | |
| 112 | + | margin-bottom: 2.5rem; | |
| 113 | + | background-color: #40363a; | |
| 114 | + | } | |
| 115 | + | ||
| 116 | + | blockquote p { | |
| 117 | + | margin-bottom: 0; | |
| 118 | + | } | |
| 119 | + | ||
| 120 | + | img, video { | |
| 121 | + | height: auto; | |
| 122 | + | max-width: 100%; | |
| 123 | + | margin-top: 0px; | |
| 124 | + | margin-bottom: 2.5rem; | |
| 125 | + | } | |
| 126 | + | ||
| 127 | + | /* Pre and Code */ | |
| 128 | + | pre { | |
| 129 | + | background-color: #40363a; | |
| 130 | + | display: block; | |
| 131 | + | padding: 1em; | |
| 132 | + | overflow-x: auto; | |
| 133 | + | margin-top: 0px; | |
| 134 | + | margin-bottom: 2.5rem; | |
| 135 | + | font-size: 0.9em; | |
| 136 | + | } | |
| 137 | + | ||
| 138 | + | code, kbd, samp { | |
| 139 | + | font-size: 0.9em; | |
| 140 | + | padding: 0 0.5em; | |
| 141 | + | background-color: #40363a; | |
| 142 | + | white-space: pre-wrap; | |
| 143 | + | } | |
| 144 | + | ||
| 145 | + | pre > code { | |
| 146 | + | padding: 0; | |
| 147 | + | background-color: transparent; | |
| 148 | + | white-space: pre; | |
| 149 | + | font-size: 1em; | |
| 150 | + | } | |
| 151 | + | ||
| 152 | + | /* Tables */ | |
| 153 | + | table { | |
| 154 | + | text-align: justify; | |
| 155 | + | width: 100%; | |
| 156 | + | border-collapse: collapse; | |
| 157 | + | margin-bottom: 2rem; | |
| 158 | + | } | |
| 159 | + | ||
| 160 | + | td, th { | |
| 161 | + | padding: 0.5em; | |
| 162 | + | border-bottom: 1px solid #40363a; | |
| 163 | + | } | |
| 164 | + | ||
| 165 | + | /* Buttons, forms and input */ | |
| 166 | + | input, textarea { | |
| 167 | + | border: 1px solid #d9d8dc; | |
| 168 | + | } | |
| 169 | + | input:focus, textarea:focus { | |
| 170 | + | border: 1px solid #eb99a1; | |
| 171 | + | } | |
| 172 | + | ||
| 173 | + | textarea { | |
| 174 | + | width: 100%; | |
| 175 | + | } | |
| 176 | + | ||
| 177 | + | .button, button, input[type=submit], input[type=reset], input[type=button], input[type=file]::file-selector-button { | |
| 178 | + | display: inline-block; | |
| 179 | + | padding: 5px 10px; | |
| 180 | + | text-align: center; | |
| 181 | + | text-decoration: none; | |
| 182 | + | white-space: nowrap; | |
| 183 | + | background-color: #eb99a1; | |
| 184 | + | color: #120c0e; | |
| 185 | + | border-radius: 1px; | |
| 186 | + | border: 1px solid #eb99a1; | |
| 187 | + | cursor: pointer; | |
| 188 | + | box-sizing: border-box; | |
| 189 | + | } | |
| 190 | + | .button[disabled], button[disabled], input[type=submit][disabled], input[type=reset][disabled], input[type=button][disabled], input[type=file]::file-selector-button[disabled] { | |
| 191 | + | cursor: default; | |
| 192 | + | opacity: 0.5; | |
| 193 | + | } | |
| 194 | + | .button:hover, button:hover, input[type=submit]:hover, input[type=reset]:hover, input[type=button]:hover, input[type=file]::file-selector-button:hover { | |
| 195 | + | background-color: #DA4453; | |
| 196 | + | color: #120c0e; | |
| 197 | + | outline: 0; | |
| 198 | + | } | |
| 199 | + | .button:focus-visible, button:focus-visible, input[type=submit]:focus-visible, input[type=reset]:focus-visible, input[type=button]:focus-visible, input[type=file]::file-selector-button:focus-visible { | |
| 200 | + | outline-style: solid; | |
| 201 | + | outline-width: 2px; | |
| 202 | + | } | |
| 203 | + | ||
| 204 | + | textarea, select, input { | |
| 205 | + | color: #d9d8dc; | |
| 206 | + | padding: 6px 10px; /* The 6px vertically centers text on FF, ignored by Webkit */ | |
| 207 | + | margin-bottom: 10px; | |
| 208 | + | background-color: #40363a; | |
| 209 | + | border: 1px solid #40363a; | |
| 210 | + | border-radius: 4px; | |
| 211 | + | box-shadow: none; | |
| 212 | + | box-sizing: border-box; | |
| 213 | + | } | |
| 214 | + | textarea:focus, select:focus, input:focus { | |
| 215 | + | border: 1px solid #eb99a1; | |
| 216 | + | outline: 0; | |
| 217 | + | } | |
| 218 | + | ||
| 219 | + | input[type=checkbox]:focus { | |
| 220 | + | outline: 1px dotted #eb99a1; | |
| 221 | + | } | |
| 222 | + | ||
| 223 | + | label, legend, fieldset { | |
| 224 | + | display: block; | |
| 225 | + | margin-bottom: 0.5rem; | |
| 226 | + | font-weight: 600; | |
| 227 | + | } | |
Aassets/show.css
| @@ -0,0 +1,94 @@ | |||
|---|---|---|---|
| 1 | + | @import "normalize.css"; | |
| 2 | + | @import "sakura-vader.css"; | |
| 3 | + | @import "highlight.css"; | |
| 4 | + | ||
| 5 | + | hr { | |
| 6 | + | width: 100%; | |
| 7 | + | } | |
| 8 | + | ||
| 9 | + | body { | |
| 10 | + | box-sizing: border-box; | |
| 11 | + | width: 100%; | |
| 12 | + | height: 100vh; | |
| 13 | + | max-width: 100%; | |
| 14 | + | max-height: 100vh; | |
| 15 | + | ||
| 16 | + | display: flex; | |
| 17 | + | flex-direction: row; | |
| 18 | + | ||
| 19 | + | #content { | |
| 20 | + | box-sizing: border-box; | |
| 21 | + | flex-grow: 1; | |
| 22 | + | height: 100%; | |
| 23 | + | overflow: auto; | |
| 24 | + | border: 1px solid #40363a; | |
| 25 | + | border-right: unset; | |
| 26 | + | display: flex; | |
| 27 | + | flex-direction: column; | |
| 28 | + | ||
| 29 | + | #filename { | |
| 30 | + | padding-left: 1rem; | |
| 31 | + | } | |
| 32 | + | ||
| 33 | + | pre { | |
| 34 | + | box-sizing: border-box; | |
| 35 | + | margin: 0; | |
| 36 | + | flex-grow: 1; | |
| 37 | + | overflow: auto; | |
| 38 | + | max-width: 100%; | |
| 39 | + | max-height: 100%; | |
| 40 | + | } | |
| 41 | + | ||
| 42 | + | #mediabox { | |
| 43 | + | flex-grow: 1; | |
| 44 | + | display: flex; | |
| 45 | + | justify-content: center; | |
| 46 | + | } | |
| 47 | + | } | |
| 48 | + | ||
| 49 | + | #sidebar { | |
| 50 | + | box-sizing: border-box; | |
| 51 | + | box-sizing: border-box; | |
| 52 | + | flex-shrink: 0; | |
| 53 | + | padding: 1rem; | |
| 54 | + | width: 30rem; | |
| 55 | + | height: 100%; | |
| 56 | + | border: 1px solid #40363a; | |
| 57 | + | display: flex; | |
| 58 | + | flex-direction: column; | |
| 59 | + | ||
| 60 | + | h3 { | |
| 61 | + | margin-top: 0; | |
| 62 | + | text-align: center; | |
| 63 | + | } | |
| 64 | + | ||
| 65 | + | form { | |
| 66 | + | align-self: center; | |
| 67 | + | } | |
| 68 | + | ||
| 69 | + | } | |
| 70 | + | } | |
| 71 | + | ||
| 72 | + | audio { | |
| 73 | + | max-width: 40rem; | |
| 74 | + | min-width: 0; | |
| 75 | + | flex-grow: 1; | |
| 76 | + | align-self: center; | |
| 77 | + | } | |
| 78 | + | ||
| 79 | + | img, | |
| 80 | + | video { | |
| 81 | + | object-fit: scale-down; | |
| 82 | + | } | |
| 83 | + | ||
| 84 | + | dialog { | |
| 85 | + | background-color: #120c0e; | |
| 86 | + | color: #d9d8dc; | |
| 87 | + | border-color: #eb99a1; | |
| 88 | + | ||
| 89 | + | form { | |
| 90 | + | display: flex; | |
| 91 | + | flex-direction: column; | |
| 92 | + | align-items: center; | |
| 93 | + | } | |
| 94 | + | } | |
Abun.lock
| @@ -0,0 +1,85 @@ | |||
|---|---|---|---|
| 1 | + | { | |
| 2 | + | "lockfileVersion": 1, | |
| 3 | + | "workspaces": { | |
| 4 | + | "": { | |
| 5 | + | "name": "zigbin", | |
| 6 | + | "dependencies": { | |
| 7 | + | "@chneau/elysia-compression": "^1.0.11", | |
| 8 | + | "@elysiajs/static": "^1.2.0", | |
| 9 | + | "elysia": "latest", | |
| 10 | + | "file-type": "^20.0.1", | |
| 11 | + | "highlight.js": "^11.11.1", | |
| 12 | + | "pkcs7-padding": "^0.1.1", | |
| 13 | + | "utf-8-validate": "^6.0.5", | |
| 14 | + | }, | |
| 15 | + | "devDependencies": { | |
| 16 | + | "@types/pkcs7-padding": "^0.1.3", | |
| 17 | + | "@types/utf-8-validate": "^5.0.2", | |
| 18 | + | "bun-types": "latest", | |
| 19 | + | }, | |
| 20 | + | }, | |
| 21 | + | }, | |
| 22 | + | "packages": { | |
| 23 | + | "@chneau/elysia-compression": ["@chneau/elysia-compression@1.0.11", "", { "dependencies": { "elysia": "^1.1.9" } }, "sha512-J4wfz5Qs68p/DNvkg5DXxo8sxJKSibzaUBP4W56uvqgs9NtHG0LC6tnyv7sAPEjNKzh21l6kinMoXbtVDp/KSQ=="], | |
| 24 | + | ||
| 25 | + | "@elysiajs/static": ["@elysiajs/static@1.2.0", "", { "dependencies": { "node-cache": "^5.1.2" }, "peerDependencies": { "elysia": ">= 1.2.0" } }, "sha512-oLpAi8c+maPpA0XhhK3BELaIjIG+nXg/K9p8cFfW4q5ayRD59a3MOMOOGgpiXZkHJzLPWcouhhyyLAYtaANW4g=="], | |
| 26 | + | ||
| 27 | + | "@sinclair/typebox": ["@sinclair/typebox@0.34.15", "", {}, "sha512-xeIzl3h1Znn9w/LTITqpiwag0gXjA+ldi2ZkXIBxGEppGCW211Tza+eL6D4pKqs10bj5z2umBWk5WL6spQ2OCQ=="], | |
| 28 | + | ||
| 29 | + | "@tokenizer/inflate": ["@tokenizer/inflate@0.2.6", "", { "dependencies": { "debug": "^4.3.7", "fflate": "^0.8.2", "token-types": "^6.0.0" } }, "sha512-SdR/i05U7Xhnsq36iyIq/ZiGGw4PKzw4ww3bOq80Pjj4wyXpqyTcgrgdDdGlcatnlvzNJx8CQw3hp6QZvkUwhA=="], | |
| 30 | + | ||
| 31 | + | "@tokenizer/token": ["@tokenizer/token@0.3.0", "", {}, "sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A=="], | |
| 32 | + | ||
| 33 | + | "@types/node": ["@types/node@22.13.0", "", { "dependencies": { "undici-types": "~6.20.0" } }, "sha512-ClIbNe36lawluuvq3+YYhnIN2CELi+6q8NpnM7PYp4hBn/TatfboPgVSm2rwKRfnV2M+Ty9GWDFI64KEe+kysA=="], | |
| 34 | + | ||
| 35 | + | "@types/pkcs7-padding": ["@types/pkcs7-padding@0.1.3", "", { "dependencies": { "@types/node": "*" } }, "sha512-rh4Tsp96Rj4KRBkRMHTZNsyjMBYT7lmHPr9Q6iZiRP1rrXxFzZKj3CpWP15a9ZE5O6BO3KqL0WsHzc+FGYYF1g=="], | |
| 36 | + | ||
| 37 | + | "@types/utf-8-validate": ["@types/utf-8-validate@5.0.2", "", { "dependencies": { "@types/node": "*" } }, "sha512-ta7cOkEiNr0RGKARljNBaI7E1GBIr3VwS9RrSoQRmbdv1RVq7Q6VhjSGmQHYNt3nHn051qZBKKrpnw7cnEMDuQ=="], | |
| 38 | + | ||
| 39 | + | "@types/ws": ["@types/ws@8.5.14", "", { "dependencies": { "@types/node": "*" } }, "sha512-bd/YFLW+URhBzMXurx7lWByOu+xzU9+kb3RboOteXYDfW+tr+JZa99OyNmPINEGB/ahzKrEuc8rcv4gnpJmxTw=="], | |
| 40 | + | ||
| 41 | + | "bun-types": ["bun-types@1.2.2", "", { "dependencies": { "@types/node": "*", "@types/ws": "~8.5.10" } }, "sha512-RCbMH5elr9gjgDGDhkTTugA21XtJAy/9jkKe/G3WR2q17VPGhcquf9Sir6uay9iW+7P/BV0CAHA1XlHXMAVKHg=="], | |
| 42 | + | ||
| 43 | + | "clone": ["clone@2.1.2", "", {}, "sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w=="], | |
| 44 | + | ||
| 45 | + | "cookie": ["cookie@1.0.2", "", {}, "sha512-9Kr/j4O16ISv8zBBhJoi4bXOYNTkFLOqSL3UDB0njXxCXNezjeyVrJyGOWtgfs/q2km1gwBcfH8q1yEGoMYunA=="], | |
| 46 | + | ||
| 47 | + | "debug": ["debug@4.4.0", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA=="], | |
| 48 | + | ||
| 49 | + | "elysia": ["elysia@1.2.10", "", { "dependencies": { "@sinclair/typebox": "^0.34.13", "cookie": "^1.0.2", "memoirist": "^0.2.0", "openapi-types": "^12.1.3" }, "peerDependencies": { "typescript": ">= 5.0.0" }, "optionalPeers": ["typescript"] }, "sha512-QcNl2FjhHFRpKaqy1NoMpyCjJ7OcKBnHwLUkqGu09QwIV84PFb82ILvYJG4GS1RbGv76OA50luaqBLrM3SLZ2w=="], | |
| 50 | + | ||
| 51 | + | "fflate": ["fflate@0.8.2", "", {}, "sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A=="], | |
| 52 | + | ||
| 53 | + | "file-type": ["file-type@20.0.1", "", { "dependencies": { "@tokenizer/inflate": "^0.2.6", "strtok3": "^10.2.0", "token-types": "^6.0.0", "uint8array-extras": "^1.4.0" } }, "sha512-R80W+NZ+s1M8PsC8L7xn+J0N72vEyxstMH+4vwIyt5B7ojfG80h/nKu2Qw/NYJTIYyGUeaPNLnzfJd78SWnG3A=="], | |
| 54 | + | ||
| 55 | + | "highlight.js": ["highlight.js@11.11.1", "", {}, "sha512-Xwwo44whKBVCYoliBQwaPvtd/2tYFkRQtXDWj1nackaV2JPXx3L0+Jvd8/qCJ2p+ML0/XVkJ2q+Mr+UVdpJK5w=="], | |
| 56 | + | ||
| 57 | + | "ieee754": ["ieee754@1.2.1", "", {}, "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA=="], | |
| 58 | + | ||
| 59 | + | "memoirist": ["memoirist@0.2.0", "", {}, "sha512-DA1V11OWsKmYjgYHfT1luus0FtTjUbILfI9s5M+ckK29tBLON6GDhH5GwxDz7E1ou4Bdzm9vhbeCaRAWxwG+0g=="], | |
| 60 | + | ||
| 61 | + | "ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="], | |
| 62 | + | ||
| 63 | + | "node-cache": ["node-cache@5.1.2", "", { "dependencies": { "clone": "2.x" } }, "sha512-t1QzWwnk4sjLWaQAS8CHgOJ+RAfmHpxFWmc36IWTiWHQfs0w5JDMBS1b1ZxQteo0vVVuWJvIUKHDkkeK7vIGCg=="], | |
| 64 | + | ||
| 65 | + | "node-gyp-build": ["node-gyp-build@4.8.4", "", { "bin": { "node-gyp-build": "bin.js", "node-gyp-build-optional": "optional.js", "node-gyp-build-test": "build-test.js" } }, "sha512-LA4ZjwlnUblHVgq0oBF3Jl/6h/Nvs5fzBLwdEF4nuxnFdsfajde4WfxtJr3CaiH+F6ewcIB/q4jQ4UzPyid+CQ=="], | |
| 66 | + | ||
| 67 | + | "openapi-types": ["openapi-types@12.1.3", "", {}, "sha512-N4YtSYJqghVu4iek2ZUvcN/0aqH1kRDuNqzcycDxhOUpg7GdvLa2F3DgS6yBNhInhv2r/6I0Flkn7CqL8+nIcw=="], | |
| 68 | + | ||
| 69 | + | "peek-readable": ["peek-readable@6.1.0", "", {}, "sha512-1H5ECS+rPH35Foh4JD/XohQKWsx6Jzn37ESOVTFuCSoI8wMB9r2e2aDSLgHSiyucVrPfoc0DRiipBEP1gr9wLw=="], | |
| 70 | + | ||
| 71 | + | "pkcs7-padding": ["pkcs7-padding@0.1.1", "", {}, "sha512-tM/sUOL5FdH7x6gSLHTNDFi1bAut/EXGFp/Ih8uRkVd2IdXwb5QWnxBgQXn6buadj1pLi3CYKCnrxb+XoTz+Ww=="], | |
| 72 | + | ||
| 73 | + | "strtok3": ["strtok3@10.2.0", "", { "dependencies": { "@tokenizer/token": "^0.3.0", "peek-readable": "^6.1.0" } }, "sha512-S884oIGzokq3LkL/6jXw5c5oJXRiGt4jB42cuWdaooJdxFMSn99snaShh3cQmJx3jALV2eoyW3rV/TJxwOaBPA=="], | |
| 74 | + | ||
| 75 | + | "token-types": ["token-types@6.0.0", "", { "dependencies": { "@tokenizer/token": "^0.3.0", "ieee754": "^1.2.1" } }, "sha512-lbDrTLVsHhOMljPscd0yitpozq7Ga2M5Cvez5AjGg8GASBjtt6iERCAJ93yommPmz62fb45oFIXHEZ3u9bfJEA=="], | |
| 76 | + | ||
| 77 | + | "typescript": ["typescript@5.7.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-84MVSjMEHP+FQRPy3pX9sTVV/INIex71s9TL2Gm5FG/WG1SqXeKyZ0k7/blY/4FdOzI12CBy1vGc4og/eus0fw=="], | |
| 78 | + | ||
| 79 | + | "uint8array-extras": ["uint8array-extras@1.4.0", "", {}, "sha512-ZPtzy0hu4cZjv3z5NW9gfKnNLjoz4y6uv4HlelAjDK7sY/xOkKZv9xK/WQpcsBB3jEybChz9DPC2U/+cusjJVQ=="], | |
| 80 | + | ||
| 81 | + | "undici-types": ["undici-types@6.20.0", "", {}, "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg=="], | |
| 82 | + | ||
| 83 | + | "utf-8-validate": ["utf-8-validate@6.0.5", "", { "dependencies": { "node-gyp-build": "^4.3.0" } }, "sha512-EYZR+OpIXp9Y1eG1iueg8KRsY8TuT8VNgnanZ0uA3STqhHQTLwbl+WX76/9X5OY12yQubymBpaBSmMPkSTQcKA=="], | |
| 84 | + | } | |
| 85 | + | } | |
Mpackage.json
| @@ -1,14 +1,24 @@ | |||
|---|---|---|---|
| 1 | 1 | { | |
| 2 | 2 | "name": "zigbin", | |
| 3 | - | "version": "1.0.50", | |
| 3 | + | "version": "1.0.0", | |
| 4 | 4 | "scripts": { | |
| 5 | - | "test": "echo \"Error: no test specified\" && exit 1", | |
| 6 | - | "dev": "bun run --watch src/index.ts" | |
| 5 | + | "build": "bun build src/client*.ts --outdir assets/dist", | |
| 6 | + | "dev": "bun run build && bun run --watch src/index.ts", | |
| 7 | + | "prod": "bun run build && bun run src/index.ts" | |
| 7 | 8 | }, | |
| 8 | 9 | "dependencies": { | |
| 9 | - | "elysia": "latest" | |
| 10 | + | "@chneau/elysia-compression": "^1.0.11", | |
| 11 | + | "@elysiajs/static": "^1.2.0", | |
| 12 | + | "@elysiajs/html": "^1.2.0", | |
| 13 | + | "elysia": "^1.2.0", | |
| 14 | + | "file-type": "^20.0.1", | |
| 15 | + | "highlight.js": "^11.11.1", | |
| 16 | + | "pkcs7-padding": "^0.1.1", | |
| 17 | + | "utf-8-validate": "^6.0.5" | |
| 10 | 18 | }, | |
| 11 | 19 | "devDependencies": { | |
| 20 | + | "@types/pkcs7-padding": "^0.1.3", | |
| 21 | + | "@types/utf-8-validate": "^5.0.2", | |
| 12 | 22 | "bun-types": "latest" | |
| 13 | 23 | }, | |
| 14 | 24 | "module": "src/index.js" | |
Asrc/client-index.ts
| @@ -0,0 +1,76 @@ | |||
|---|---|---|---|
| 1 | + | async function encrypt(content: Uint8Array, password: string): Promise<ArrayBuffer> { | |
| 2 | + | const iv = crypto.getRandomValues(new Uint8Array(16)) | |
| 3 | + | const salt = crypto.getRandomValues(new Uint8Array(16)) | |
| 4 | + | const keyMaterial = await crypto.subtle.importKey( | |
| 5 | + | "raw", | |
| 6 | + | new TextEncoder().encode(password), | |
| 7 | + | { name: "PBKDF2" }, | |
| 8 | + | false, | |
| 9 | + | ["deriveBits", "deriveKey"] | |
| 10 | + | ) | |
| 11 | + | const key = await crypto.subtle.deriveKey( | |
| 12 | + | { | |
| 13 | + | name: "PBKDF2", | |
| 14 | + | salt: salt, | |
| 15 | + | iterations: 100000, | |
| 16 | + | hash: "SHA-512" | |
| 17 | + | }, | |
| 18 | + | keyMaterial, | |
| 19 | + | { name: "AES-CBC", length: 256 }, | |
| 20 | + | false, | |
| 21 | + | ["encrypt"] | |
| 22 | + | ) | |
| 23 | + | const encryptedContent = await crypto.subtle.encrypt( | |
| 24 | + | { name: "AES-CBC", iv: iv }, | |
| 25 | + | key, | |
| 26 | + | content | |
| 27 | + | ) | |
| 28 | + | return new Uint8Array([...salt, ...iv, ...new Uint8Array(encryptedContent)]).buffer | |
| 29 | + | } | |
| 30 | + | ||
| 31 | + | async function uploadFile() { | |
| 32 | + | const passwordInput = document.getElementById("password") as HTMLInputElement | null | |
| 33 | + | const fileInput = document.getElementById("file") as HTMLInputElement | null | |
| 34 | + | const filenameInput = document.getElementById("filename") as HTMLInputElement | null | |
| 35 | + | const encryptedInput = document.getElementById("encrypted") as HTMLInputElement | null | |
| 36 | + | const form = document.getElementById("uploadForm") as HTMLFormElement | null | |
| 37 | + | ||
| 38 | + | if (!passwordInput || !fileInput || !filenameInput || !encryptedInput || !form) { | |
| 39 | + | console.error("Missing required elements.") | |
| 40 | + | console.error(passwordInput, fileInput, filenameInput, encryptedInput, form) | |
| 41 | + | return | |
| 42 | + | } | |
| 43 | + | ||
| 44 | + | const password = passwordInput.value; | |
| 45 | + | if (!password) { | |
| 46 | + | form.submit() | |
| 47 | + | return | |
| 48 | + | } | |
| 49 | + | ||
| 50 | + | const file = fileInput.files?.[0]; | |
| 51 | + | if (!file) { | |
| 52 | + | return | |
| 53 | + | } | |
| 54 | + | ||
| 55 | + | const content = await file.bytes() | |
| 56 | + | const encryptedContent = await encrypt(content, password) | |
| 57 | + | ||
| 58 | + | const myFile = new File([encryptedContent], file.name) | |
| 59 | + | const dataTransfer = new DataTransfer() | |
| 60 | + | dataTransfer.items.add(myFile) | |
| 61 | + | ||
| 62 | + | fileInput.files = dataTransfer.files | |
| 63 | + | encryptedInput.checked = true | |
| 64 | + | console.log("encryption done") | |
| 65 | + | form.submit() | |
| 66 | + | } | |
| 67 | + | ||
| 68 | + | (document.getElementById("uploadForm") as HTMLFormElement | null)?.reset() | |
| 69 | + | ||
| 70 | + | const jsguards = document.getElementsByClassName("jsguard") | |
| 71 | + | for (let i = 0; i < jsguards.length; i++) { | |
| 72 | + | jsguards[i].remove() | |
| 73 | + | } | |
| 74 | + | ||
| 75 | + | window.uploadFile = uploadFile | |
| 76 | + | ||
Asrc/client-show.ts
| @@ -0,0 +1,139 @@ | |||
|---|---|---|---|
| 1 | + | import { fileTypeFromBuffer } from 'file-type' | |
| 2 | + | import hljs from 'highlight.js' | |
| 3 | + | import { humanFileSize } from './shared' | |
| 4 | + | ||
| 5 | + | async function decrypt(encryptedContent: ArrayBuffer, password: string): Promise<ArrayBuffer> { | |
| 6 | + | const encryptedArray = new Uint8Array(encryptedContent) | |
| 7 | + | const salt = encryptedArray.slice(0, 16) | |
| 8 | + | const iv = encryptedArray.slice(16, 32) | |
| 9 | + | const keyMaterial = await crypto.subtle.importKey( | |
| 10 | + | "raw", | |
| 11 | + | new TextEncoder().encode(password), | |
| 12 | + | { name: "PBKDF2" }, | |
| 13 | + | false, | |
| 14 | + | ["deriveBits", "deriveKey"] | |
| 15 | + | ) | |
| 16 | + | const key = await crypto.subtle.deriveKey( | |
| 17 | + | { | |
| 18 | + | name: "PBKDF2", | |
| 19 | + | salt: salt, | |
| 20 | + | iterations: 100000, | |
| 21 | + | hash: "SHA-512" | |
| 22 | + | }, | |
| 23 | + | keyMaterial, | |
| 24 | + | { name: "AES-CBC", length: 256 }, | |
| 25 | + | false, | |
| 26 | + | ["decrypt"] | |
| 27 | + | ) | |
| 28 | + | const decryptedContent = await crypto.subtle.decrypt( | |
| 29 | + | { name: "AES-CBC", iv: iv }, | |
| 30 | + | key, | |
| 31 | + | encryptedArray.slice(32) | |
| 32 | + | ) | |
| 33 | + | return decryptedContent | |
| 34 | + | } | |
| 35 | + | ||
| 36 | + | function isValidUTF8(buf: ArrayBuffer) { | |
| 37 | + | try { | |
| 38 | + | new TextDecoder("utf-8", { fatal: true }).decode(buf) | |
| 39 | + | return true | |
| 40 | + | } catch (e) { | |
| 41 | + | return false | |
| 42 | + | } | |
| 43 | + | } | |
| 44 | + | ||
| 45 | + | async function showContent(content: ArrayBuffer, filetype: string) { | |
| 46 | + | const filesize = document.getElementById("filesize") as HTMLDivElement | null | |
| 47 | + | if (filesize) { | |
| 48 | + | filesize.textContent = `size: ${humanFileSize(content.byteLength)}` | |
| 49 | + | } | |
| 50 | + | ||
| 51 | + | const mediaBox = document.getElementById("mediabox") as HTMLDivElement | null | |
| 52 | + | if (!mediaBox) { | |
| 53 | + | console.error("Missing mediaBox element.") | |
| 54 | + | return | |
| 55 | + | } | |
| 56 | + | let preview: HTMLElement = document.createElement("div") | |
| 57 | + | preview.textContent = "This file can't be previewed" | |
| 58 | + | if (isValidUTF8(content) && filetype !== "blob") { | |
| 59 | + | preview = document.createElement("pre") | |
| 60 | + | if (filetype === "none") { | |
| 61 | + | preview.textContent = new TextDecoder().decode(content) | |
| 62 | + | } else { | |
| 63 | + | preview.innerHTML = hljs.highlight(new TextDecoder("utf-8").decode(content), { language: filetype }).value | |
| 64 | + | } | |
| 65 | + | } else { | |
| 66 | + | const filetype = await fileTypeFromBuffer(content) | |
| 67 | + | if (filetype) { | |
| 68 | + | const blob = new Blob([content], { type: filetype.mime }) | |
| 69 | + | const url = URL.createObjectURL(blob) | |
| 70 | + | if (filetype.mime.startsWith("audio/")) { | |
| 71 | + | preview = document.createElement("audio") | |
| 72 | + | preview.setAttribute("controls", "") | |
| 73 | + | preview.setAttribute("src", url) | |
| 74 | + | } else if (filetype.mime.startsWith("video/")) { | |
| 75 | + | preview = document.createElement("video") | |
| 76 | + | preview.setAttribute("controls", "") | |
| 77 | + | preview.setAttribute("src", url) | |
| 78 | + | } else if (filetype.mime.startsWith("image/")) { | |
| 79 | + | preview = document.createElement("img") | |
| 80 | + | preview.setAttribute("src", url) | |
| 81 | + | } | |
| 82 | + | } | |
| 83 | + | } | |
| 84 | + | mediaBox.innerHTML = "" | |
| 85 | + | mediaBox.appendChild(preview) | |
| 86 | + | } | |
| 87 | + | ||
| 88 | + | let content: ArrayBuffer | |
| 89 | + | let filetype: string | |
| 90 | + | let filename: string | |
| 91 | + | ||
| 92 | + | async function onPasswordSubmit() { | |
| 93 | + | const passwordInput = document.getElementById("password") as HTMLInputElement | null | |
| 94 | + | const passwordLabel = document.getElementById("password-label") as HTMLLabelElement | null | |
| 95 | + | if (!passwordInput || !passwordLabel) { | |
| 96 | + | console.error("Missing passwordInput or passwordLabel element.") | |
| 97 | + | return | |
| 98 | + | } | |
| 99 | + | try { | |
| 100 | + | content = await decrypt(content, passwordInput.value) | |
| 101 | + | } catch (e) { | |
| 102 | + | passwordLabel.textContent = "Incorrect password" | |
| 103 | + | return | |
| 104 | + | } | |
| 105 | + | const dialog = document.getElementById("password-dialog") as HTMLDialogElement | null | |
| 106 | + | dialog?.close() | |
| 107 | + | const downloadForm = document.getElementById("download-form") as HTMLFormElement | null | |
| 108 | + | downloadForm?.setAttribute("action", "") | |
| 109 | + | downloadForm?.addEventListener("submit", (e) => { | |
| 110 | + | e.preventDefault() | |
| 111 | + | const blob = new Blob([content]) | |
| 112 | + | const url = URL.createObjectURL(blob) | |
| 113 | + | const link = document.createElement("a") | |
| 114 | + | link.download = filename | |
| 115 | + | link.href = url | |
| 116 | + | link.click(); | |
| 117 | + | }) | |
| 118 | + | showContent(content, filetype) | |
| 119 | + | } | |
| 120 | + | ||
| 121 | + | const uuid = window.location.pathname.split("/").filter((x) => x !== "").reverse()[0] | |
| 122 | + | ||
| 123 | + | fetch("/raw/" + uuid + "?ignore_password=true").then(async (response) => { | |
| 124 | + | content = await response.arrayBuffer() | |
| 125 | + | const mediaBox = document.getElementById("mediabox") | |
| 126 | + | if(mediaBox) { | |
| 127 | + | mediaBox.innerHTML = "" | |
| 128 | + | } | |
| 129 | + | filetype = response.headers.get("filetype") || "none" | |
| 130 | + | filename = response.headers.get("filename") || "" | |
| 131 | + | if (response.headers.get("encrypted") === "true") { | |
| 132 | + | const dialog = document.getElementById("password-dialog") as HTMLDialogElement | null | |
| 133 | + | dialog?.showModal() | |
| 134 | + | } else { | |
| 135 | + | showContent(content, filetype) | |
| 136 | + | } | |
| 137 | + | }); | |
| 138 | + | ||
| 139 | + | (window as any).onPasswordSubmit = onPasswordSubmit | |
Asrc/components.tsx
| @@ -0,0 +1,201 @@ | |||
|---|---|---|---|
| 1 | + | import { Html } from '@elysiajs/html' | |
| 2 | + | import { PropsWithChildren } from '@kitajs/html' | |
| 3 | + | import { escapeHTML } from 'bun' | |
| 4 | + | import { fileTypeFromBuffer } from 'file-type' | |
| 5 | + | import hljs from 'highlight.js' | |
| 6 | + | import isValidUTF8 from 'utf-8-validate' | |
| 7 | + | import { humanFileSize } from './shared' | |
| 8 | + | ||
| 9 | + | const filetypes = ["none", "blob"].concat(hljs.listLanguages().sort()) | |
| 10 | + | ||
| 11 | + | function Template(props: PropsWithChildren<{ css: string }>) { | |
| 12 | + | return ( | |
| 13 | + | <> | |
| 14 | + | {'<!doctype html>'} | |
| 15 | + | <html lang="en"> | |
| 16 | + | <head> | |
| 17 | + | <title>⚡Zigbin⚡</title> | |
| 18 | + | <meta charset="UTF-8" /> | |
| 19 | + | <meta name="viewport" content="width=device-width, initial-scale=1" /> | |
| 20 | + | <link rel="stylesheet" href={props.css} /> | |
| 21 | + | <link rel="icon" href="data:image/svg+xml,<svg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 100 100%22><text y=%22.9em%22 font-size=%2290%22>📂</text></svg>" /> | |
| 22 | + | </head> | |
| 23 | + | <body>{props.children}</body> | |
| 24 | + | </html> | |
| 25 | + | </> | |
| 26 | + | ) | |
| 27 | + | } | |
| 28 | + | ||
| 29 | + | export function NotFound() { | |
| 30 | + | return ( | |
| 31 | + | <Template css="/default.css"> | |
| 32 | + | <h1>404</h1> | |
| 33 | + | <p>File not found</p> | |
| 34 | + | </Template> | |
| 35 | + | ) | |
| 36 | + | } | |
| 37 | + | ||
| 38 | + | export function Index(hostname: string, withJs: boolean) { | |
| 39 | + | let body = ( | |
| 40 | + | <> | |
| 41 | + | {withJs ? | |
| 42 | + | <div style="text-align: center">JS version with client-side encryption, for the version without js an with server-side encryption, go <a href="/">here</a>.</div> | |
| 43 | + | : <div style="text-align: center">JS-less version with server-side encryption, for the version with client-side encryption, go to <a href="/js">/js</a>.</div> | |
| 44 | + | } | |
| 45 | + | <hr /> | |
| 46 | + | <form action={withJs ? "/upload?withJs=true" : "/upload"} id="uploadForm" method="post" enctype="multipart/form-data" onsubmit={withJs ? "uploadFile(); return false" : undefined}> | |
| 47 | + | <input type="reset" value="Reset form" /> | |
| 48 | + | <input required={true} type="file" id="file" name="file" /> | |
| 49 | + | <label for="filename">File name override:</label> | |
| 50 | + | <small>Optional. If empty, will use the uploaded file name.</small> | |
| 51 | + | <input type="text" id="filename" name="filename" placeholder="File name" /> | |
| 52 | + | <label for="filetype">File type:</label> | |
| 53 | + | <small>Used for syntax highlighting, if the file is only containing valid UTF-8 characters. Otherwise it will always be stored as a binary blob. Can select <code>blob</code> to force this detection.</small> | |
| 54 | + | <select size="10" required={true} id="filetype" name="filetype"> | |
| 55 | + | { | |
| 56 | + | filetypes.map((filetype, i) => ( | |
| 57 | + | <option selected={i === 0} value={filetype}>{filetype}</option> | |
| 58 | + | )) | |
| 59 | + | } | |
| 60 | + | </select> | |
| 61 | + | <label for="password">Password:</label> | |
| 62 | + | <small>Optional. If set, file will be encrypted and requires the given password to open.</small> | |
| 63 | + | <input type="password" id="password" name="password" placeholder="Password" /> | |
| 64 | + | <div> | |
| 65 | + | <label for="encrypted">Already encrypted:</label> | |
| 66 | + | <input type="checkbox" id="encrypted" name="encrypted" /> | |
| 67 | + | </div> | |
| 68 | + | <small>If set, file is already encrypted with an algorithm like in the python file available below. Password is ignored in this case.</small> | |
| 69 | + | <input type="submit" value="Upload File" /> | |
| 70 | + | <hr /> | |
| 71 | + | <h3>curl guide</h3> | |
| 72 | + | You can use curl to upload and download files<br /> | |
| 73 | + | Upload: | |
| 74 | + | <pre>curl {hostname}upload \{"\n"} | |
| 75 | + | -F file=@/path/to/file \{"\n"} | |
| 76 | + | -F filetype="plaintext" \{"\n"} | |
| 77 | + | # optional{"\n"} | |
| 78 | + | -F filename="file name" \{"\n"} | |
| 79 | + | # optional{"\n"} | |
| 80 | + | -F password="mypassword" \{"\n"} | |
| 81 | + | # optional{"\n"} | |
| 82 | + | -F encrypted="on"</pre> | |
| 83 | + | <p> | |
| 84 | + | If you upload an already encrypted file, you should set the <code>encrypted="on"</code> parameter and encrypt it in a compatible way, so the frontend/backend can also decrypt it. | |
| 85 | + | You can use this python script to encrypt a file locally: <a href="/encrypt.py">encrypt.py</a> | |
| 86 | + | </p> | |
| 87 | + | <br /> | |
| 88 | + | Download: | |
| 89 | + | <pre>curl {hostname}raw/$uuid?ignore_password=true/false \{"\n"} | |
| 90 | + | # optional, if encrypted with password{"\n"} | |
| 91 | + | --cookie "password=mypassword"</pre> | |
| 92 | + | <p> | |
| 93 | + | If ignore_password is set to true, then encrypted files can be downloaded directly (in encrypted form) without supplying the password. | |
| 94 | + | </p> | |
| 95 | + | </form> | |
| 96 | + | </> | |
| 97 | + | ) | |
| 98 | + | if (withJs) { | |
| 99 | + | body = ( | |
| 100 | + | <> | |
| 101 | + | <div class="jsguard">JS seems to be disabled, enable it and refesh the page, or go back to<a href="/">here</a> for the JS-less version.</div> | |
| 102 | + | <div class="jsguard"> | |
| 103 | + | {body} | |
| 104 | + | </div> | |
| 105 | + | <script src="/dist/client-index.js" /> | |
| 106 | + | </> | |
| 107 | + | ) | |
| 108 | + | } | |
| 109 | + | return ( | |
| 110 | + | <Template css="/default.css"> | |
| 111 | + | <h1>⚡Zigbin⚡</h1> | |
| 112 | + | {body} | |
| 113 | + | </Template> | |
| 114 | + | ) | |
| 115 | + | } | |
| 116 | + | ||
| 117 | + | ||
| 118 | + | export function DecryptFile(filename: string, uuid: string) { | |
| 119 | + | return ( | |
| 120 | + | <Template css="/default.css"> | |
| 121 | + | <h1>Encrypted file: {filename}</h1> | |
| 122 | + | <form action={`/set-cookie/${uuid}`} method="post"> | |
| 123 | + | <label for="password">Enter a Password to decrypt the file</label> | |
| 124 | + | <input type="password" name="password" /> | |
| 125 | + | <input type="submit" value="Submit" /> | |
| 126 | + | </form> | |
| 127 | + | </Template> | |
| 128 | + | ) | |
| 129 | + | } | |
| 130 | + | ||
| 131 | + | export function WrongPassword() { | |
| 132 | + | return ( | |
| 133 | + | <Template css="/default.css"> | |
| 134 | + | <h1>Incorrect password</h1> | |
| 135 | + | <p>Reload page to try again</p> | |
| 136 | + | </Template> | |
| 137 | + | ) | |
| 138 | + | } | |
| 139 | + | ||
| 140 | + | export async function ShowFile(filename: string, uuid: string, content: Uint8Array | null, filetype: string) { | |
| 141 | + | //if content is null, this is for the js frontend | |
| 142 | + | let preview; | |
| 143 | + | if (!content) { | |
| 144 | + | preview = <>Please wait for the file to load</> | |
| 145 | + | } else { | |
| 146 | + | preview = <>This file can't be previewed</> | |
| 147 | + | if (isValidUTF8(Buffer.from(content)) && filetype !== "blob") { | |
| 148 | + | if (filetype === "none") { | |
| 149 | + | preview = <pre>{escapeHTML(new TextDecoder("utf-8").decode(content))}</pre> | |
| 150 | + | } else { | |
| 151 | + | preview = <pre>{hljs.highlight(new TextDecoder("utf-8").decode(content), { language: filetype }).value}</pre> | |
| 152 | + | } | |
| 153 | + | } else { | |
| 154 | + | const filetype = await fileTypeFromBuffer(content) | |
| 155 | + | if (filetype) { | |
| 156 | + | const base64Content = `data:${filetype.mime};base64,${Buffer.from(content).toString("base64")}`; | |
| 157 | + | if (filetype.mime.startsWith("audio/")) { | |
| 158 | + | preview = <audio controls="" src={base64Content} /> | |
| 159 | + | } else if (filetype.mime.startsWith("video/")) { | |
| 160 | + | preview = <video controls src={base64Content} /> | |
| 161 | + | } else if (filetype.mime.startsWith("image/")) { | |
| 162 | + | preview = <img src={base64Content} /> | |
| 163 | + | } | |
| 164 | + | } | |
| 165 | + | } | |
| 166 | + | } | |
| 167 | + | ||
| 168 | + | return ( | |
| 169 | + | <Template css="/show.css"> | |
| 170 | + | <dialog id="password-dialog"> | |
| 171 | + | <form onsubmit="onPasswordSubmit(); return false"> | |
| 172 | + | <label id="password-label" for="password">File is encrypted, enter password to decrypt:</label> | |
| 173 | + | <input required type="password" id="password" /> | |
| 174 | + | <input type="submit" value="Submit" /> | |
| 175 | + | </form> | |
| 176 | + | </dialog> | |
| 177 | + | <div id="content"> | |
| 178 | + | <div id="filename">{filename}</div> | |
| 179 | + | <div id="mediabox">{preview}</div> | |
| 180 | + | </div> | |
| 181 | + | <div id="sidebar"> | |
| 182 | + | <h3>File info</h3> | |
| 183 | + | <hr /> | |
| 184 | + | <p id="filesize">size: {content ? humanFileSize(content.byteLength) : "unknown"}</p> | |
| 185 | + | <p>declared type: {filetype}</p> | |
| 186 | + | <form id="download-form" action={`/raw/${uuid}`} method="get"> | |
| 187 | + | <button>Download</button> | |
| 188 | + | </form> | |
| 189 | + | </div> | |
| 190 | + | {content ? "" : <script src="/dist/client-show.js" />} | |
| 191 | + | </Template> | |
| 192 | + | ) | |
| 193 | + | } | |
| 194 | + | ||
| 195 | + | export function SetCookie(uuid: string) { | |
| 196 | + | return ( | |
| 197 | + | <Template css="/default.css"> | |
| 198 | + | Redirecting you back to <a href={`/show/${uuid}`}>/show/{uuid}</a> | |
| 199 | + | </Template> | |
| 200 | + | ) | |
| 201 | + | } | |
Msrc/index.ts
| @@ -1,7 +1,143 @@ | |||
|---|---|---|---|
| 1 | - | import { Elysia } from "elysia"; | |
| 1 | + | import { Elysia, StatusMap, t } from "elysia" | |
| 2 | + | import { Database } from "bun:sqlite" | |
| 3 | + | import { randomUUIDv7 } from "bun" | |
| 4 | + | import staticPlugin from "@elysiajs/static" | |
| 5 | + | import { html } from '@elysiajs/html' | |
| 6 | + | import crypto from 'crypto' | |
| 7 | + | import { DecryptFile, Index, NotFound, SetCookie, ShowFile, WrongPassword } from "./components" | |
| 2 | 8 | ||
| 3 | - | const app = new Elysia().get("/", () => "Hello Elysia").listen(3000); | |
| 4 | 9 | ||
| 5 | - | console.log( | |
| 6 | - | `🦊 Elysia is running at ${app.server?.hostname}:${app.server?.port}` | |
| 7 | - | ); | |
| 10 | + | const db = new Database("./db.sqlite") | |
| 11 | + | db.exec("PRAGMA foreign_keys = ON") | |
| 12 | + | db.exec("PRAGMA journal_mode = WAL2") | |
| 13 | + | db.exec("CREATE TABLE IF NOT EXISTS files (uuid TEXT PRIMARY KEY, filename TEXT NOT NULL, content BLOB NOT NULL, filetype TEXT NOT NULL, encrypted INTEGER NOT NULL) STRICT") | |
| 14 | + | db.exec("PRAGMA optimize") | |
| 15 | + | ||
| 16 | + | ||
| 17 | + | function encrypt(content: Uint8Array, password: string): Buffer<ArrayBuffer> { | |
| 18 | + | let iv = crypto.randomBytes(16) | |
| 19 | + | const salt = crypto.randomBytes(16) | |
| 20 | + | const key = crypto.pbkdf2Sync(password, salt, 100000, 32, "sha512") | |
| 21 | + | let cipher = crypto.createCipheriv('aes-256-cbc', key, iv) | |
| 22 | + | let encrypted_content = Buffer.concat([salt, iv, cipher.update(content), cipher.final()]) | |
| 23 | + | return encrypted_content | |
| 24 | + | } | |
| 25 | + | ||
| 26 | + | function decrypt(encrypted_content: Uint8Array, password: string): Buffer { | |
| 27 | + | const salt = encrypted_content.slice(0, 16) | |
| 28 | + | const iv = encrypted_content.slice(16, 32) | |
| 29 | + | const key = crypto.pbkdf2Sync(password, salt, 100000, 32, "sha512") | |
| 30 | + | let decipher = crypto.createDecipheriv('aes-256-cbc', key, iv) | |
| 31 | + | let content = Buffer.concat([decipher.update(encrypted_content.slice(32)), decipher.final()]) | |
| 32 | + | return content | |
| 33 | + | } | |
| 34 | + | ||
| 35 | + | const app = new Elysia({ | |
| 36 | + | serve: { | |
| 37 | + | maxRequestBodySize: 1024 * 1024 * 1024 // 1GB | |
| 38 | + | } | |
| 39 | + | }) | |
| 40 | + | .use(staticPlugin({ assets: "./assets", prefix: "/" })) | |
| 41 | + | .use(html()) | |
| 42 | + | .get("/", ({ server }) => Index(server?.url.toString()!, false)) | |
| 43 | + | .get("/js", ({ server }) => Index(server?.url.toString()!, true)) | |
| 44 | + | .post("/upload", async ({ set, body, query }) => { | |
| 45 | + | ||
| 46 | + | const uuid = randomUUIDv7() | |
| 47 | + | let content = Buffer.from(await body.file.bytes()) | |
| 48 | + | let encrypted = false; | |
| 49 | + | if (body.encrypted === "on") { | |
| 50 | + | encrypted = true | |
| 51 | + | } else if (body.password) { | |
| 52 | + | content = encrypt(content, body.password) | |
| 53 | + | encrypted = true | |
| 54 | + | } | |
| 55 | + | db.exec("INSERT INTO files (uuid, filename, content, filetype, encrypted) VALUES (?, ?, ?, ?, ?)", [uuid, body.filename || body.file.name, content, body.filetype, encrypted]) | |
| 56 | + | set.status = StatusMap["See Other"] | |
| 57 | + | if (query.withJs) { | |
| 58 | + | set.headers["location"] = `/show-js/${uuid}` | |
| 59 | + | } else { | |
| 60 | + | set.headers["location"] = `/show/${uuid}` | |
| 61 | + | } | |
| 62 | + | return `Created with id: ${uuid}` | |
| 63 | + | ||
| 64 | + | }, { | |
| 65 | + | body: t.Object({ file: t.File(), filename: t.Optional(t.String()), filetype: t.String()/*todo:verifiy via t.Enum*/, password: t.Optional(t.String()), encrypted: t.Optional(t.String()) }), | |
| 66 | + | query: t.Object({ withJs: t.Optional(t.Boolean()) }) | |
| 67 | + | }) | |
| 68 | + | .get("/show/:uuid", async ({ set, params, cookie }) => { | |
| 69 | + | ||
| 70 | + | const result = db.prepare("SELECT filename, content, filetype, encrypted FROM files WHERE uuid = ?").get(params.uuid) as { filename: string, content: Uint8Array, filetype: string, encrypted: boolean } || null | |
| 71 | + | if (!result) { | |
| 72 | + | set.status = StatusMap["Not Found"] | |
| 73 | + | return NotFound() | |
| 74 | + | } | |
| 75 | + | if (result.encrypted) { | |
| 76 | + | const password = cookie.password.value | |
| 77 | + | if (!password) { | |
| 78 | + | return DecryptFile(result.filename, params.uuid) | |
| 79 | + | } else { | |
| 80 | + | try { | |
| 81 | + | result.content = decrypt(result.content, password) | |
| 82 | + | } catch (e) { | |
| 83 | + | set.status = StatusMap["Forbidden"] | |
| 84 | + | set.headers["set-cookie"] = [ | |
| 85 | + | `password=; Path=/show/${params.uuid}; SameSite=lax; HttpOnly; Expires=Thu, 01 Jan 1970 00:00:00 GMT`, | |
| 86 | + | `password=; Path=/raw/${params.uuid}; SameSite=lax; HttpOnly; Expires=Thu, 01 Jan 1970 00:00:00 GMT` | |
| 87 | + | ] | |
| 88 | + | return WrongPassword() | |
| 89 | + | } | |
| 90 | + | } | |
| 91 | + | } | |
| 92 | + | return ShowFile(result.filename, params.uuid, result.content, result.filetype) | |
| 93 | + | ||
| 94 | + | }, { params: t.Object({ uuid: t.String() }) }) | |
| 95 | + | .get("/show-js/:uuid", async ({ set, params, cookie }) => { | |
| 96 | + | ||
| 97 | + | const result = db.prepare("SELECT filename, content, filetype, encrypted FROM files WHERE uuid = ?").get(params.uuid) as { filename: string, content: Uint8Array, filetype: string, encrypted: boolean } || null | |
| 98 | + | if (!result) { | |
| 99 | + | set.status = StatusMap["Not Found"] | |
| 100 | + | return NotFound() | |
| 101 | + | } | |
| 102 | + | return ShowFile(result.filename, params.uuid, null, result.filetype) | |
| 103 | + | ||
| 104 | + | }, { params: t.Object({ uuid: t.String() }) }) | |
| 105 | + | .post("/set-cookie/:uuid", ({ set, body, params }) => { | |
| 106 | + | ||
| 107 | + | set.headers["set-cookie"] = [ | |
| 108 | + | `password=${body.password}; Path=/show/${params.uuid}; SameSite=lax; HttpOnly`, | |
| 109 | + | `password=${body.password}; Path=/raw/${params.uuid}; SameSite=lax; HttpOnly` | |
| 110 | + | ] | |
| 111 | + | set.headers["location"] = `/show/${params.uuid}` | |
| 112 | + | set.status = StatusMap["See Other"] | |
| 113 | + | return SetCookie(params.uuid) | |
| 114 | + | ||
| 115 | + | }, { body: t.Object({ password: t.String() }), params: t.Object({ uuid: t.String() }) }) | |
| 116 | + | .get("/raw/:uuid", ({ set, params, cookie, query }) => { | |
| 117 | + | const result = db.prepare("SELECT content, filename, encrypted, filetype FROM files WHERE uuid = ?").get(params.uuid) as { content: Uint8Array, filename: string, encrypted: boolean, filetype: string } || null | |
| 118 | + | if (!result) { | |
| 119 | + | set.status = StatusMap["Not Found"] | |
| 120 | + | return "File not found" | |
| 121 | + | } | |
| 122 | + | if (result.encrypted && query.ignore_password !== "true") { | |
| 123 | + | if (!cookie.password.value) { | |
| 124 | + | set.status = StatusMap["Unauthorized"] | |
| 125 | + | return "This file is encrypted, set the cookie \"password\" with the correct password to allow the server to decrypt it" | |
| 126 | + | } | |
| 127 | + | try { | |
| 128 | + | result.content = decrypt(result.content, cookie.password.value) | |
| 129 | + | } catch (e) { | |
| 130 | + | set.status = StatusMap["Forbidden"] | |
| 131 | + | return "Incorrect password" | |
| 132 | + | } | |
| 133 | + | } | |
| 134 | + | set.headers["encrypted"] = result.encrypted ? "true" : "false" | |
| 135 | + | set.headers["filetype"] = result.filetype | |
| 136 | + | set.headers["filename"] = result.filename | |
| 137 | + | set.headers["content-disposition"] = `inline; filename=${result.filename}` | |
| 138 | + | return result.content | |
| 139 | + | ||
| 140 | + | }, { params: t.Object({ uuid: t.String() }), cookie: t.Object({ password: t.Optional(t.String()) }), query: t.Object({ ignore_password: t.Optional(t.String()) }) }) | |
| 141 | + | .listen(3000) | |
| 142 | + | ||
| 143 | + | console.log(`⚡ Zigbin is running at ${app.server?.hostname}:${app.server?.port} ⚡`) | |
Mtsconfig.json
| @@ -1,103 +1,18 @@ | |||
|---|---|---|---|
| 1 | 1 | { | |
| 2 | 2 | "compilerOptions": { | |
| 3 | 3 | /* Visit https://aka.ms/tsconfig to read more about this file */ | |
| 4 | - | ||
| 5 | - | /* Projects */ | |
| 6 | - | // "incremental": true, /* Save .tsbuildinfo files to allow for incremental compilation of projects. */ | |
| 7 | - | // "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */ | |
| 8 | - | // "tsBuildInfoFile": "./.tsbuildinfo", /* Specify the path to .tsbuildinfo incremental compilation file. */ | |
| 9 | - | // "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects. */ | |
| 10 | - | // "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */ | |
| 11 | - | // "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */ | |
| 12 | - | ||
| 13 | - | /* Language and Environment */ | |
| 14 | - | "target": "ES2021", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */ | |
| 15 | - | // "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */ | |
| 16 | - | // "jsx": "preserve", /* Specify what JSX code is generated. */ | |
| 17 | - | // "experimentalDecorators": true, /* Enable experimental support for TC39 stage 2 draft decorators. */ | |
| 18 | - | // "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */ | |
| 19 | - | // "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h'. */ | |
| 20 | - | // "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */ | |
| 21 | - | // "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using 'jsx: react-jsx*'. */ | |
| 22 | - | // "reactNamespace": "", /* Specify the object invoked for 'createElement'. This only applies when targeting 'react' JSX emit. */ | |
| 23 | - | // "noLib": true, /* Disable including any library files, including the default lib.d.ts. */ | |
| 24 | - | // "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */ | |
| 25 | - | // "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */ | |
| 26 | - | ||
| 27 | - | /* Modules */ | |
| 28 | - | "module": "ES2022", /* Specify what module code is generated. */ | |
| 29 | - | // "rootDir": "./", /* Specify the root folder within your source files. */ | |
| 30 | - | "moduleResolution": "node", /* Specify how TypeScript looks up a file from a given module specifier. */ | |
| 31 | - | // "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */ | |
| 32 | - | // "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */ | |
| 33 | - | // "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */ | |
| 34 | - | // "typeRoots": [], /* Specify multiple folders that act like './node_modules/@types'. */ | |
| 35 | - | "types": ["bun-types"], /* Specify type package names to be included without being referenced in a source file. */ | |
| 36 | - | // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ | |
| 37 | - | // "moduleSuffixes": [], /* List of file name suffixes to search when resolving a module. */ | |
| 38 | - | // "resolveJsonModule": true, /* Enable importing .json files. */ | |
| 39 | - | // "noResolve": true, /* Disallow 'import's, 'require's or '<reference>'s from expanding the number of files TypeScript should add to a project. */ | |
| 40 | - | ||
| 41 | - | /* JavaScript Support */ | |
| 42 | - | // "allowJs": true, /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */ | |
| 43 | - | // "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */ | |
| 44 | - | // "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from 'node_modules'. Only applicable with 'allowJs'. */ | |
| 45 | - | ||
| 46 | - | /* Emit */ | |
| 47 | - | // "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */ | |
| 48 | - | // "declarationMap": true, /* Create sourcemaps for d.ts files. */ | |
| 49 | - | // "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */ | |
| 50 | - | // "sourceMap": true, /* Create source map files for emitted JavaScript files. */ | |
| 51 | - | // "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If 'declaration' is true, also designates a file that bundles all .d.ts output. */ | |
| 52 | - | // "outDir": "./", /* Specify an output folder for all emitted files. */ | |
| 53 | - | // "removeComments": true, /* Disable emitting comments. */ | |
| 54 | - | // "noEmit": true, /* Disable emitting files from a compilation. */ | |
| 55 | - | // "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */ | |
| 56 | - | // "importsNotUsedAsValues": "remove", /* Specify emit/checking behavior for imports that are only used for types. */ | |
| 57 | - | // "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */ | |
| 58 | - | // "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */ | |
| 59 | - | // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ | |
| 60 | - | // "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */ | |
| 61 | - | // "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */ | |
| 62 | - | // "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */ | |
| 63 | - | // "newLine": "crlf", /* Set the newline character for emitting files. */ | |
| 64 | - | // "stripInternal": true, /* Disable emitting declarations that have '@internal' in their JSDoc comments. */ | |
| 65 | - | // "noEmitHelpers": true, /* Disable generating custom helper functions like '__extends' in compiled output. */ | |
| 66 | - | // "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */ | |
| 67 | - | // "preserveConstEnums": true, /* Disable erasing 'const enum' declarations in generated code. */ | |
| 68 | - | // "declarationDir": "./", /* Specify the output directory for generated declaration files. */ | |
| 69 | - | // "preserveValueImports": true, /* Preserve unused imported values in the JavaScript output that would otherwise be removed. */ | |
| 70 | - | ||
| 71 | - | /* Interop Constraints */ | |
| 72 | - | // "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */ | |
| 73 | - | // "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */ | |
| 74 | - | "esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */ | |
| 75 | - | // "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */ | |
| 76 | - | "forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */ | |
| 77 | - | ||
| 78 | - | /* Type Checking */ | |
| 79 | - | "strict": true, /* Enable all strict type-checking options. */ | |
| 80 | - | // "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied 'any' type. */ | |
| 81 | - | // "strictNullChecks": true, /* When type checking, take into account 'null' and 'undefined'. */ | |
| 82 | - | // "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */ | |
| 83 | - | // "strictBindCallApply": true, /* Check that the arguments for 'bind', 'call', and 'apply' methods match the original function. */ | |
| 84 | - | // "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */ | |
| 85 | - | // "noImplicitThis": true, /* Enable error reporting when 'this' is given the type 'any'. */ | |
| 86 | - | // "useUnknownInCatchVariables": true, /* Default catch clause variables as 'unknown' instead of 'any'. */ | |
| 87 | - | // "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */ | |
| 88 | - | // "noUnusedLocals": true, /* Enable error reporting when local variables aren't read. */ | |
| 89 | - | // "noUnusedParameters": true, /* Raise an error when a function parameter isn't read. */ | |
| 90 | - | // "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */ | |
| 91 | - | // "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */ | |
| 92 | - | // "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */ | |
| 93 | - | // "noUncheckedIndexedAccess": true, /* Add 'undefined' to a type when accessed using an index. */ | |
| 94 | - | // "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */ | |
| 95 | - | // "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type. */ | |
| 96 | - | // "allowUnusedLabels": true, /* Disable error reporting for unused labels. */ | |
| 97 | - | // "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */ | |
| 98 | - | ||
| 99 | - | /* Completeness */ | |
| 100 | - | // "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */ | |
| 101 | - | "skipLibCheck": true /* Skip type checking all .d.ts files. */ | |
| 4 | + | "target": "ES2021", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */ | |
| 5 | + | "module": "ES2022", /* Specify what module code is generated. */ | |
| 6 | + | "moduleResolution": "node", /* Specify how TypeScript looks up a file from a given module specifier. */ | |
| 7 | + | "types": [ | |
| 8 | + | "bun-types" | |
| 9 | + | ], /* Specify type package names to be included without being referenced in a source file. */ | |
| 10 | + | "esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */ | |
| 11 | + | "forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */ | |
| 12 | + | "strict": true, /* Enable all strict type-checking options. */ | |
| 13 | + | "skipLibCheck": true, /* Skip type checking all .d.ts files. */ | |
| 14 | + | "jsx": "react", | |
| 15 | + | "jsxFactory": "Html.createElement", | |
| 16 | + | "jsxFragmentFactory": "Html.Fragment" | |
| 102 | 17 | } | |
| 103 | - | } | |
| 18 | + | } | |