├── .drone.jsonnet ├── .gitignore ├── LICENSE ├── README.md ├── bin ├── initdb.sh ├── nextcloud-config ├── nextcloud-config.php ├── nextcloud-cron ├── occ-runner ├── pg_dumpall.sh ├── php-runner ├── php.sh ├── psql.sh ├── service.nginx.sh ├── service.php-fpm.sh ├── service.postgresql.sh └── service.redis.sh ├── build.sh ├── config ├── config.php ├── env ├── fastcgi_params ├── nginx.conf ├── php-fpm.conf ├── php.ini ├── postgresql.conf └── redis.conf ├── download.sh ├── hooks ├── access-change ├── access-change.py ├── backup-pre-stop ├── installer.py ├── octools.py ├── postgres.py ├── restore-post-start ├── restore-pre-start ├── storage-change └── storage-change.py ├── meta ├── hooks │ ├── configure │ ├── install │ ├── post-refresh │ └── pre-refresh └── snap.yaml ├── nginx ├── build.sh └── nginx.sh ├── package.sh ├── php ├── Dockerfile ├── build.sh ├── php-fpm.sh └── php.sh ├── postgresql ├── Dockerfile ├── bin │ ├── initdb.sh │ ├── pg_ctl.sh │ ├── pg_dumpall.sh │ └── psql.sh ├── build.sh └── pgbin │ ├── pg_dump │ └── postgres ├── python ├── Dockerfile ├── build.sh ├── python └── requirements.txt ├── redis ├── bin │ └── redis.sh ├── build.sh └── test.sh └── test ├── .gitignore ├── __init__.py ├── conftest.py ├── deps.sh ├── requirements.txt ├── test.odt ├── test.py ├── ui.py └── upgrade.py /.drone.jsonnet: -------------------------------------------------------------------------------- 1 | local name = "nextcloud"; 2 | local browser = "chrome"; 3 | local nextcloud = "31.0.5"; 4 | local redis = "7.0.15"; 5 | local nginx = "1.24.0"; 6 | local platform = '25.02'; 7 | local selenium = '4.21.0-20240517'; 8 | local deployer = 'https://github.com/syncloud/store/releases/download/4/syncloud-release'; 9 | local distro_default = "buster"; 10 | local distros = ["bookworm", "buster"]; 11 | 12 | local build(arch, test_ui, dind) = [{ 13 | kind: "pipeline", 14 | type: "docker", 15 | name: arch, 16 | platform: { 17 | os: "linux", 18 | arch: arch 19 | }, 20 | steps: [ 21 | { 22 | name: "version", 23 | image: "debian:buster-slim", 24 | commands: [ 25 | "echo $DRONE_BUILD_NUMBER > version" 26 | ] 27 | }, 28 | { 29 | name: "download", 30 | image: "debian:buster-slim", 31 | commands: [ 32 | "./download.sh " + nextcloud 33 | ] 34 | }, 35 | { 36 | name: "nginx", 37 | image: "docker:" + dind, 38 | commands: [ 39 | "./nginx/build.sh " + nginx 40 | ], 41 | volumes: [ 42 | { 43 | name: "dockersock", 44 | path: "/var/run" 45 | } 46 | ] 47 | }, 48 | 49 | { 50 | name: "redis", 51 | image: "redis:" + redis, 52 | commands: [ 53 | "./redis/build.sh" 54 | ] 55 | }, 56 | { 57 | name: "redis test", 58 | image: "debian:buster-slim", 59 | commands: [ 60 | "./redis/test.sh" 61 | ] 62 | }, 63 | { 64 | name: "postgresql", 65 | image: "docker:" + dind, 66 | commands: [ 67 | "./postgresql/build.sh" 68 | ], 69 | volumes: [ 70 | { 71 | name: "dockersock", 72 | path: "/var/run" 73 | } 74 | ] 75 | }, 76 | { 77 | name: "python", 78 | image: "docker:" + dind, 79 | commands: [ 80 | "./python/build.sh" 81 | ], 82 | volumes: [ 83 | { 84 | name: "dockersock", 85 | path: "/var/run" 86 | } 87 | ] 88 | }, 89 | { 90 | name: "php", 91 | image: "docker:" + dind, 92 | commands: [ 93 | "./php/build.sh" 94 | ], 95 | volumes: [ 96 | { 97 | name: "dockersock", 98 | path: "/var/run" 99 | } 100 | ] 101 | }, 102 | { 103 | name: "build", 104 | image: "debian:buster-slim", 105 | commands: [ 106 | "./build.sh" 107 | ] 108 | }, 109 | { 110 | name: "package", 111 | image: "debian:buster-slim", 112 | commands: [ 113 | "VERSION=$(cat version)", 114 | "./package.sh " + name + " $VERSION " 115 | ] 116 | }] + [ 117 | { 118 | name: "test " + distro, 119 | image: "python:3.8-slim-buster", 120 | commands: [ 121 | "APP_ARCHIVE_PATH=$(realpath $(cat package.name))", 122 | "cd test", 123 | "./deps.sh", 124 | "py.test -x -s test.py --distro=" + distro + " --domain=" + distro + ".com --app-archive-path=$APP_ARCHIVE_PATH --device-host=" + name + "." + distro + ".com --app=" + name + " --arch=" + arch 125 | ] 126 | } for distro in distros 127 | ] + ( if test_ui then [ 128 | { 129 | name: "selenium", 130 | image: "selenium/standalone-" + browser + ":" + selenium, 131 | detach: true, 132 | environment: { 133 | SE_NODE_SESSION_TIMEOUT: "999999", 134 | START_XVFB: "true" 135 | }, 136 | volumes: [{ 137 | name: "shm", 138 | path: "/dev/shm" 139 | }], 140 | commands: [ 141 | "cat /etc/hosts", 142 | "getent hosts " + name + ".buster.com | sed 's/" + name +".buster.com/auth.buster.com/g' | sudo tee -a /etc/hosts", 143 | "cat /etc/hosts", 144 | "/opt/bin/entry_point.sh" 145 | ] 146 | }, 147 | 148 | { 149 | name: "selenium-video", 150 | image: "selenium/video:ffmpeg-4.3.1-20220208", 151 | detach: true, 152 | environment: { 153 | DISPLAY_CONTAINER_NAME: "selenium", 154 | FILE_NAME: "video.mkv" 155 | }, 156 | volumes: [ 157 | { 158 | name: "shm", 159 | path: "/dev/shm" 160 | }, 161 | { 162 | name: "videos", 163 | path: "/videos" 164 | } 165 | ] 166 | }, 167 | { 168 | name: "test-ui", 169 | image: "python:3.8-slim-buster", 170 | commands: [ 171 | "cd test", 172 | "./deps.sh", 173 | "py.test -x -s ui.py --distro=buster --ui-mode=desktop --domain=buster.com --device-host=" + name + ".buster.com --app=" + name + " --browser=" + browser, 174 | ], 175 | volumes: [{ 176 | name: "videos", 177 | path: "/videos" 178 | }] 179 | } 180 | 181 | ] else [] ) +[ 182 | { 183 | name: "test-upgrade", 184 | image: "python:3.8-slim-buster", 185 | commands: [ 186 | "APP_ARCHIVE_PATH=$(realpath $(cat package.name))", 187 | "cd test", 188 | "./deps.sh", 189 | "py.test -x -s upgrade.py --distro=buster --ui-mode=desktop --domain=buster.com --app-archive-path=$APP_ARCHIVE_PATH --device-host=" + name + ".buster.com --app=" + name + " --browser=" + browser, 190 | ] 191 | }, 192 | { 193 | name: "upload", 194 | image: "debian:buster-slim", 195 | environment: { 196 | AWS_ACCESS_KEY_ID: { 197 | from_secret: "AWS_ACCESS_KEY_ID" 198 | }, 199 | AWS_SECRET_ACCESS_KEY: { 200 | from_secret: "AWS_SECRET_ACCESS_KEY" 201 | }, 202 | SYNCLOUD_TOKEN: { 203 | from_secret: "SYNCLOUD_TOKEN" 204 | } 205 | }, 206 | commands: [ 207 | "PACKAGE=$(cat package.name)", 208 | "apt update && apt install -y wget", 209 | "wget " + deployer + "-" + arch + " -O release --progress=dot:giga", 210 | "chmod +x release", 211 | "./release publish -f $PACKAGE -b $DRONE_BRANCH" 212 | ], 213 | when: { 214 | branch: ["stable", "master"], 215 | event: [ "push" ] 216 | } 217 | }, 218 | { 219 | name: "promote", 220 | image: "debian:buster-slim", 221 | environment: { 222 | AWS_ACCESS_KEY_ID: { 223 | from_secret: "AWS_ACCESS_KEY_ID" 224 | }, 225 | AWS_SECRET_ACCESS_KEY: { 226 | from_secret: "AWS_SECRET_ACCESS_KEY" 227 | }, 228 | SYNCLOUD_TOKEN: { 229 | from_secret: "SYNCLOUD_TOKEN" 230 | } 231 | }, 232 | commands: [ 233 | "apt update && apt install -y wget", 234 | "wget " + deployer + "-" + arch + " -O release --progress=dot:giga", 235 | "chmod +x release", 236 | "./release promote -n " + name + " -a $(dpkg --print-architecture)" 237 | ], 238 | when: { 239 | branch: ["stable"], 240 | event: ["push"] 241 | } 242 | }, 243 | { 244 | name: "artifact", 245 | image: "appleboy/drone-scp:1.6.4", 246 | settings: { 247 | host: { 248 | from_secret: "artifact_host" 249 | }, 250 | username: "artifact", 251 | key: { 252 | from_secret: "artifact_key" 253 | }, 254 | timeout: "2m", 255 | command_timeout: "2m", 256 | target: "/home/artifact/repo/" + name + "/${DRONE_BUILD_NUMBER}-" + arch, 257 | source: "artifact/*", 258 | strip_components: 1 259 | }, 260 | when: { 261 | status: [ "failure", "success" ], 262 | event: [ "push" ] 263 | } 264 | } 265 | ], 266 | trigger: { 267 | event: [ 268 | "push", 269 | "pull_request" 270 | ] 271 | }, 272 | services: [ 273 | { 274 | name: "docker", 275 | image: "docker:" + dind, 276 | privileged: true, 277 | volumes: [ 278 | { 279 | name: "dockersock", 280 | path: "/var/run" 281 | } 282 | ] 283 | }] + [ 284 | { 285 | name: name + "."+distro+".com", 286 | image: "syncloud/platform-"+distro+"-" + arch + ":" + platform, 287 | privileged: true, 288 | volumes: [ 289 | { 290 | name: "dbus", 291 | path: "/var/run/dbus" 292 | }, 293 | { 294 | name: "dev", 295 | path: "/dev" 296 | } 297 | ] 298 | } for distro in distros 299 | ], 300 | volumes: [ 301 | { 302 | name: "dbus", 303 | host: { 304 | path: "/var/run/dbus" 305 | } 306 | }, 307 | { 308 | name: "dev", 309 | host: { 310 | path: "/dev" 311 | } 312 | }, 313 | { 314 | name: "shm", 315 | temp: {} 316 | }, 317 | { 318 | name: "dockersock", 319 | temp: {} 320 | }, 321 | { 322 | name: "videos", 323 | temp: {} 324 | }, 325 | ] 326 | }]; 327 | 328 | build("amd64", true, "20.10.21-dind") + 329 | build("arm64", false, "19.03.8-dind") 330 | 331 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | dist 3 | *.pyc 4 | *.egg-info 5 | build 6 | *.tar.gz 7 | *.tar.bz2 8 | rootfs 9 | *.log 10 | *.iml 11 | log 12 | .cache 13 | lib 14 | src/version 15 | .coin.cache 16 | geckodriver 17 | *.test.download 18 | *.test.upload 19 | test.file* 20 | venv -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 3, 29 June 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 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 General Public License is a free, copyleft license for 11 | software and other kinds of works. 12 | 13 | The licenses for most software and other practical works are designed 14 | to take away your freedom to share and change the works. By contrast, 15 | the GNU General Public License is intended to guarantee your freedom to 16 | share and change all versions of a program--to make sure it remains free 17 | software for all its users. We, the Free Software Foundation, use the 18 | GNU General Public License for most of our software; it applies also to 19 | any other work released this way by its authors. You can apply it to 20 | your programs, too. 21 | 22 | When we speak of free software, we are referring to freedom, not 23 | price. Our General Public Licenses are designed to make sure that you 24 | have the freedom to distribute copies of free software (and charge for 25 | them if you wish), that you receive source code or can get it if you 26 | want it, that you can change the software or use pieces of it in new 27 | free programs, and that you know you can do these things. 28 | 29 | To protect your rights, we need to prevent others from denying you 30 | these rights or asking you to surrender the rights. Therefore, you have 31 | certain responsibilities if you distribute copies of the software, or if 32 | you modify it: responsibilities to respect the freedom of others. 33 | 34 | For example, if you distribute copies of such a program, whether 35 | gratis or for a fee, you must pass on to the recipients the same 36 | freedoms that you received. You must make sure that they, too, receive 37 | or can get the source code. And you must show them these terms so they 38 | know their rights. 39 | 40 | Developers that use the GNU GPL protect your rights with two steps: 41 | (1) assert copyright on the software, and (2) offer you this License 42 | giving you legal permission to copy, distribute and/or modify it. 43 | 44 | For the developers' and authors' protection, the GPL clearly explains 45 | that there is no warranty for this free software. For both users' and 46 | authors' sake, the GPL requires that modified versions be marked as 47 | changed, so that their problems will not be attributed erroneously to 48 | authors of previous versions. 49 | 50 | Some devices are designed to deny users access to install or run 51 | modified versions of the software inside them, although the manufacturer 52 | can do so. This is fundamentally incompatible with the aim of 53 | protecting users' freedom to change the software. The systematic 54 | pattern of such abuse occurs in the area of products for individuals to 55 | use, which is precisely where it is most unacceptable. Therefore, we 56 | have designed this version of the GPL to prohibit the practice for those 57 | products. If such problems arise substantially in other domains, we 58 | stand ready to extend this provision to those domains in future versions 59 | of the GPL, as needed to protect the freedom of users. 60 | 61 | Finally, every program is threatened constantly by software patents. 62 | States should not allow patents to restrict development and use of 63 | software on general-purpose computers, but in those that do, we wish to 64 | avoid the special danger that patents applied to a free program could 65 | make it effectively proprietary. To prevent this, the GPL assures that 66 | patents cannot be used to render the program non-free. 67 | 68 | The precise terms and conditions for copying, distribution and 69 | modification follow. 70 | 71 | TERMS AND CONDITIONS 72 | 73 | 0. Definitions. 74 | 75 | "This License" refers to version 3 of the GNU General Public License. 76 | 77 | "Copyright" also means copyright-like laws that apply to other kinds of 78 | works, such as semiconductor masks. 79 | 80 | "The Program" refers to any copyrightable work licensed under this 81 | License. Each licensee is addressed as "you". "Licensees" and 82 | "recipients" may be individuals or organizations. 83 | 84 | To "modify" a work means to copy from or adapt all or part of the work 85 | in a fashion requiring copyright permission, other than the making of an 86 | exact copy. The resulting work is called a "modified version" of the 87 | earlier work or a work "based on" the earlier work. 88 | 89 | A "covered work" means either the unmodified Program or a work based 90 | on the Program. 91 | 92 | To "propagate" a work means to do anything with it that, without 93 | permission, would make you directly or secondarily liable for 94 | infringement under applicable copyright law, except executing it on a 95 | computer or modifying a private copy. Propagation includes copying, 96 | distribution (with or without modification), making available to the 97 | public, and in some countries other activities as well. 98 | 99 | To "convey" a work means any kind of propagation that enables other 100 | parties to make or receive copies. Mere interaction with a user through 101 | a computer network, with no transfer of a copy, is not conveying. 102 | 103 | An interactive user interface displays "Appropriate Legal Notices" 104 | to the extent that it includes a convenient and prominently visible 105 | feature that (1) displays an appropriate copyright notice, and (2) 106 | tells the user that there is no warranty for the work (except to the 107 | extent that warranties are provided), that licensees may convey the 108 | work under this License, and how to view a copy of this License. If 109 | the interface presents a list of user commands or options, such as a 110 | menu, a prominent item in the list meets this criterion. 111 | 112 | 1. Source Code. 113 | 114 | The "source code" for a work means the preferred form of the work 115 | for making modifications to it. "Object code" means any non-source 116 | form of a work. 117 | 118 | A "Standard Interface" means an interface that either is an official 119 | standard defined by a recognized standards body, or, in the case of 120 | interfaces specified for a particular programming language, one that 121 | is widely used among developers working in that language. 122 | 123 | The "System Libraries" of an executable work include anything, other 124 | than the work as a whole, that (a) is included in the normal form of 125 | packaging a Major Component, but which is not part of that Major 126 | Component, and (b) serves only to enable use of the work with that 127 | Major Component, or to implement a Standard Interface for which an 128 | implementation is available to the public in source code form. A 129 | "Major Component", in this context, means a major essential component 130 | (kernel, window system, and so on) of the specific operating system 131 | (if any) on which the executable work runs, or a compiler used to 132 | produce the work, or an object code interpreter used to run it. 133 | 134 | The "Corresponding Source" for a work in object code form means all 135 | the source code needed to generate, install, and (for an executable 136 | work) run the object code and to modify the work, including scripts to 137 | control those activities. However, it does not include the work's 138 | System Libraries, or general-purpose tools or generally available free 139 | programs which are used unmodified in performing those activities but 140 | which are not part of the work. For example, Corresponding Source 141 | includes interface definition files associated with source files for 142 | the work, and the source code for shared libraries and dynamically 143 | linked subprograms that the work is specifically designed to require, 144 | such as by intimate data communication or control flow between those 145 | subprograms and other parts of the work. 146 | 147 | The Corresponding Source need not include anything that users 148 | can regenerate automatically from other parts of the Corresponding 149 | Source. 150 | 151 | The Corresponding Source for a work in source code form is that 152 | same work. 153 | 154 | 2. Basic Permissions. 155 | 156 | All rights granted under this License are granted for the term of 157 | copyright on the Program, and are irrevocable provided the stated 158 | conditions are met. This License explicitly affirms your unlimited 159 | permission to run the unmodified Program. The output from running a 160 | covered work is covered by this License only if the output, given its 161 | content, constitutes a covered work. This License acknowledges your 162 | rights of fair use or other equivalent, as provided by copyright law. 163 | 164 | You may make, run and propagate covered works that you do not 165 | convey, without conditions so long as your license otherwise remains 166 | in force. You may convey covered works to others for the sole purpose 167 | of having them make modifications exclusively for you, or provide you 168 | with facilities for running those works, provided that you comply with 169 | the terms of this License in conveying all material for which you do 170 | not control copyright. Those thus making or running the covered works 171 | for you must do so exclusively on your behalf, under your direction 172 | and control, on terms that prohibit them from making any copies of 173 | your copyrighted material outside their relationship with you. 174 | 175 | Conveying under any other circumstances is permitted solely under 176 | the conditions stated below. Sublicensing is not allowed; section 10 177 | makes it unnecessary. 178 | 179 | 3. Protecting Users' Legal Rights From Anti-Circumvention Law. 180 | 181 | No covered work shall be deemed part of an effective technological 182 | measure under any applicable law fulfilling obligations under article 183 | 11 of the WIPO copyright treaty adopted on 20 December 1996, or 184 | similar laws prohibiting or restricting circumvention of such 185 | measures. 186 | 187 | When you convey a covered work, you waive any legal power to forbid 188 | circumvention of technological measures to the extent such circumvention 189 | is effected by exercising rights under this License with respect to 190 | the covered work, and you disclaim any intention to limit operation or 191 | modification of the work as a means of enforcing, against the work's 192 | users, your or third parties' legal rights to forbid circumvention of 193 | technological measures. 194 | 195 | 4. Conveying Verbatim Copies. 196 | 197 | You may convey verbatim copies of the Program's source code as you 198 | receive it, in any medium, provided that you conspicuously and 199 | appropriately publish on each copy an appropriate copyright notice; 200 | keep intact all notices stating that this License and any 201 | non-permissive terms added in accord with section 7 apply to the code; 202 | keep intact all notices of the absence of any warranty; and give all 203 | recipients a copy of this License along with the Program. 204 | 205 | You may charge any price or no price for each copy that you convey, 206 | and you may offer support or warranty protection for a fee. 207 | 208 | 5. Conveying Modified Source Versions. 209 | 210 | You may convey a work based on the Program, or the modifications to 211 | produce it from the Program, in the form of source code under the 212 | terms of section 4, provided that you also meet all of these conditions: 213 | 214 | a) The work must carry prominent notices stating that you modified 215 | it, and giving a relevant date. 216 | 217 | b) The work must carry prominent notices stating that it is 218 | released under this License and any conditions added under section 219 | 7. This requirement modifies the requirement in section 4 to 220 | "keep intact all notices". 221 | 222 | c) You must license the entire work, as a whole, under this 223 | License to anyone who comes into possession of a copy. This 224 | License will therefore apply, along with any applicable section 7 225 | additional terms, to the whole of the work, and all its parts, 226 | regardless of how they are packaged. This License gives no 227 | permission to license the work in any other way, but it does not 228 | invalidate such permission if you have separately received it. 229 | 230 | d) If the work has interactive user interfaces, each must display 231 | Appropriate Legal Notices; however, if the Program has interactive 232 | interfaces that do not display Appropriate Legal Notices, your 233 | work need not make them do so. 234 | 235 | A compilation of a covered work with other separate and independent 236 | works, which are not by their nature extensions of the covered work, 237 | and which are not combined with it such as to form a larger program, 238 | in or on a volume of a storage or distribution medium, is called an 239 | "aggregate" if the compilation and its resulting copyright are not 240 | used to limit the access or legal rights of the compilation's users 241 | beyond what the individual works permit. Inclusion of a covered work 242 | in an aggregate does not cause this License to apply to the other 243 | parts of the aggregate. 244 | 245 | 6. Conveying Non-Source Forms. 246 | 247 | You may convey a covered work in object code form under the terms 248 | of sections 4 and 5, provided that you also convey the 249 | machine-readable Corresponding Source under the terms of this License, 250 | in one of these ways: 251 | 252 | a) Convey the object code in, or embodied in, a physical product 253 | (including a physical distribution medium), accompanied by the 254 | Corresponding Source fixed on a durable physical medium 255 | customarily used for software interchange. 256 | 257 | b) Convey the object code in, or embodied in, a physical product 258 | (including a physical distribution medium), accompanied by a 259 | written offer, valid for at least three years and valid for as 260 | long as you offer spare parts or customer support for that product 261 | model, to give anyone who possesses the object code either (1) a 262 | copy of the Corresponding Source for all the software in the 263 | product that is covered by this License, on a durable physical 264 | medium customarily used for software interchange, for a price no 265 | more than your reasonable cost of physically performing this 266 | conveying of source, or (2) access to copy the 267 | Corresponding Source from a network server at no charge. 268 | 269 | c) Convey individual copies of the object code with a copy of the 270 | written offer to provide the Corresponding Source. This 271 | alternative is allowed only occasionally and noncommercially, and 272 | only if you received the object code with such an offer, in accord 273 | with subsection 6b. 274 | 275 | d) Convey the object code by offering access from a designated 276 | place (gratis or for a charge), and offer equivalent access to the 277 | Corresponding Source in the same way through the same place at no 278 | further charge. You need not require recipients to copy the 279 | Corresponding Source along with the object code. If the place to 280 | copy the object code is a network server, the Corresponding Source 281 | may be on a different server (operated by you or a third party) 282 | that supports equivalent copying facilities, provided you maintain 283 | clear directions next to the object code saying where to find the 284 | Corresponding Source. Regardless of what server hosts the 285 | Corresponding Source, you remain obligated to ensure that it is 286 | available for as long as needed to satisfy these requirements. 287 | 288 | e) Convey the object code using peer-to-peer transmission, provided 289 | you inform other peers where the object code and Corresponding 290 | Source of the work are being offered to the general public at no 291 | charge under subsection 6d. 292 | 293 | A separable portion of the object code, whose source code is excluded 294 | from the Corresponding Source as a System Library, need not be 295 | included in conveying the object code work. 296 | 297 | A "User Product" is either (1) a "consumer product", which means any 298 | tangible personal property which is normally used for personal, family, 299 | or household purposes, or (2) anything designed or sold for incorporation 300 | into a dwelling. In determining whether a product is a consumer product, 301 | doubtful cases shall be resolved in favor of coverage. For a particular 302 | product received by a particular user, "normally used" refers to a 303 | typical or common use of that class of product, regardless of the status 304 | of the particular user or of the way in which the particular user 305 | actually uses, or expects or is expected to use, the product. A product 306 | is a consumer product regardless of whether the product has substantial 307 | commercial, industrial or non-consumer uses, unless such uses represent 308 | the only significant mode of use of the product. 309 | 310 | "Installation Information" for a User Product means any methods, 311 | procedures, authorization keys, or other information required to install 312 | and execute modified versions of a covered work in that User Product from 313 | a modified version of its Corresponding Source. The information must 314 | suffice to ensure that the continued functioning of the modified object 315 | code is in no case prevented or interfered with solely because 316 | modification has been made. 317 | 318 | If you convey an object code work under this section in, or with, or 319 | specifically for use in, a User Product, and the conveying occurs as 320 | part of a transaction in which the right of possession and use of the 321 | User Product is transferred to the recipient in perpetuity or for a 322 | fixed term (regardless of how the transaction is characterized), the 323 | Corresponding Source conveyed under this section must be accompanied 324 | by the Installation Information. But this requirement does not apply 325 | if neither you nor any third party retains the ability to install 326 | modified object code on the User Product (for example, the work has 327 | been installed in ROM). 328 | 329 | The requirement to provide Installation Information does not include a 330 | requirement to continue to provide support service, warranty, or updates 331 | for a work that has been modified or installed by the recipient, or for 332 | the User Product in which it has been modified or installed. Access to a 333 | network may be denied when the modification itself materially and 334 | adversely affects the operation of the network or violates the rules and 335 | protocols for communication across the network. 336 | 337 | Corresponding Source conveyed, and Installation Information provided, 338 | in accord with this section must be in a format that is publicly 339 | documented (and with an implementation available to the public in 340 | source code form), and must require no special password or key for 341 | unpacking, reading or copying. 342 | 343 | 7. Additional Terms. 344 | 345 | "Additional permissions" are terms that supplement the terms of this 346 | License by making exceptions from one or more of its conditions. 347 | Additional permissions that are applicable to the entire Program shall 348 | be treated as though they were included in this License, to the extent 349 | that they are valid under applicable law. If additional permissions 350 | apply only to part of the Program, that part may be used separately 351 | under those permissions, but the entire Program remains governed by 352 | this License without regard to the additional permissions. 353 | 354 | When you convey a copy of a covered work, you may at your option 355 | remove any additional permissions from that copy, or from any part of 356 | it. (Additional permissions may be written to require their own 357 | removal in certain cases when you modify the work.) You may place 358 | additional permissions on material, added by you to a covered work, 359 | for which you have or can give appropriate copyright permission. 360 | 361 | Notwithstanding any other provision of this License, for material you 362 | add to a covered work, you may (if authorized by the copyright holders of 363 | that material) supplement the terms of this License with terms: 364 | 365 | a) Disclaiming warranty or limiting liability differently from the 366 | terms of sections 15 and 16 of this License; or 367 | 368 | b) Requiring preservation of specified reasonable legal notices or 369 | author attributions in that material or in the Appropriate Legal 370 | Notices displayed by works containing it; or 371 | 372 | c) Prohibiting misrepresentation of the origin of that material, or 373 | requiring that modified versions of such material be marked in 374 | reasonable ways as different from the original version; or 375 | 376 | d) Limiting the use for publicity purposes of names of licensors or 377 | authors of the material; or 378 | 379 | e) Declining to grant rights under trademark law for use of some 380 | trade names, trademarks, or service marks; or 381 | 382 | f) Requiring indemnification of licensors and authors of that 383 | material by anyone who conveys the material (or modified versions of 384 | it) with contractual assumptions of liability to the recipient, for 385 | any liability that these contractual assumptions directly impose on 386 | those licensors and authors. 387 | 388 | All other non-permissive additional terms are considered "further 389 | restrictions" within the meaning of section 10. If the Program as you 390 | received it, or any part of it, contains a notice stating that it is 391 | governed by this License along with a term that is a further 392 | restriction, you may remove that term. If a license document contains 393 | a further restriction but permits relicensing or conveying under this 394 | License, you may add to a covered work material governed by the terms 395 | of that license document, provided that the further restriction does 396 | not survive such relicensing or conveying. 397 | 398 | If you add terms to a covered work in accord with this section, you 399 | must place, in the relevant source files, a statement of the 400 | additional terms that apply to those files, or a notice indicating 401 | where to find the applicable terms. 402 | 403 | Additional terms, permissive or non-permissive, may be stated in the 404 | form of a separately written license, or stated as exceptions; 405 | the above requirements apply either way. 406 | 407 | 8. Termination. 408 | 409 | You may not propagate or modify a covered work except as expressly 410 | provided under this License. Any attempt otherwise to propagate or 411 | modify it is void, and will automatically terminate your rights under 412 | this License (including any patent licenses granted under the third 413 | paragraph of section 11). 414 | 415 | However, if you cease all violation of this License, then your 416 | license from a particular copyright holder is reinstated (a) 417 | provisionally, unless and until the copyright holder explicitly and 418 | finally terminates your license, and (b) permanently, if the copyright 419 | holder fails to notify you of the violation by some reasonable means 420 | prior to 60 days after the cessation. 421 | 422 | Moreover, your license from a particular copyright holder is 423 | reinstated permanently if the copyright holder notifies you of the 424 | violation by some reasonable means, this is the first time you have 425 | received notice of violation of this License (for any work) from that 426 | copyright holder, and you cure the violation prior to 30 days after 427 | your receipt of the notice. 428 | 429 | Termination of your rights under this section does not terminate the 430 | licenses of parties who have received copies or rights from you under 431 | this License. If your rights have been terminated and not permanently 432 | reinstated, you do not qualify to receive new licenses for the same 433 | material under section 10. 434 | 435 | 9. Acceptance Not Required for Having Copies. 436 | 437 | You are not required to accept this License in order to receive or 438 | run a copy of the Program. Ancillary propagation of a covered work 439 | occurring solely as a consequence of using peer-to-peer transmission 440 | to receive a copy likewise does not require acceptance. However, 441 | nothing other than this License grants you permission to propagate or 442 | modify any covered work. These actions infringe copyright if you do 443 | not accept this License. Therefore, by modifying or propagating a 444 | covered work, you indicate your acceptance of this License to do so. 445 | 446 | 10. Automatic Licensing of Downstream Recipients. 447 | 448 | Each time you convey a covered work, the recipient automatically 449 | receives a license from the original licensors, to run, modify and 450 | propagate that work, subject to this License. You are not responsible 451 | for enforcing compliance by third parties with this License. 452 | 453 | An "entity transaction" is a transaction transferring control of an 454 | organization, or substantially all assets of one, or subdividing an 455 | organization, or merging organizations. If propagation of a covered 456 | work results from an entity transaction, each party to that 457 | transaction who receives a copy of the work also receives whatever 458 | licenses to the work the party's predecessor in interest had or could 459 | give under the previous paragraph, plus a right to possession of the 460 | Corresponding Source of the work from the predecessor in interest, if 461 | the predecessor has it or can get it with reasonable efforts. 462 | 463 | You may not impose any further restrictions on the exercise of the 464 | rights granted or affirmed under this License. For example, you may 465 | not impose a license fee, royalty, or other charge for exercise of 466 | rights granted under this License, and you may not initiate litigation 467 | (including a cross-claim or counterclaim in a lawsuit) alleging that 468 | any patent claim is infringed by making, using, selling, offering for 469 | sale, or importing the Program or any portion of it. 470 | 471 | 11. Patents. 472 | 473 | A "contributor" is a copyright holder who authorizes use under this 474 | License of the Program or a work on which the Program is based. The 475 | work thus licensed is called the contributor's "contributor version". 476 | 477 | A contributor's "essential patent claims" are all patent claims 478 | owned or controlled by the contributor, whether already acquired or 479 | hereafter acquired, that would be infringed by some manner, permitted 480 | by this License, of making, using, or selling its contributor version, 481 | but do not include claims that would be infringed only as a 482 | consequence of further modification of the contributor version. For 483 | purposes of this definition, "control" includes the right to grant 484 | patent sublicenses in a manner consistent with the requirements of 485 | this License. 486 | 487 | Each contributor grants you a non-exclusive, worldwide, royalty-free 488 | patent license under the contributor's essential patent claims, to 489 | make, use, sell, offer for sale, import and otherwise run, modify and 490 | propagate the contents of its contributor version. 491 | 492 | In the following three paragraphs, a "patent license" is any express 493 | agreement or commitment, however denominated, not to enforce a patent 494 | (such as an express permission to practice a patent or covenant not to 495 | sue for patent infringement). To "grant" such a patent license to a 496 | party means to make such an agreement or commitment not to enforce a 497 | patent against the party. 498 | 499 | If you convey a covered work, knowingly relying on a patent license, 500 | and the Corresponding Source of the work is not available for anyone 501 | to copy, free of charge and under the terms of this License, through a 502 | publicly available network server or other readily accessible means, 503 | then you must either (1) cause the Corresponding Source to be so 504 | available, or (2) arrange to deprive yourself of the benefit of the 505 | patent license for this particular work, or (3) arrange, in a manner 506 | consistent with the requirements of this License, to extend the patent 507 | license to downstream recipients. "Knowingly relying" means you have 508 | actual knowledge that, but for the patent license, your conveying the 509 | covered work in a country, or your recipient's use of the covered work 510 | in a country, would infringe one or more identifiable patents in that 511 | country that you have reason to believe are valid. 512 | 513 | If, pursuant to or in connection with a single transaction or 514 | arrangement, you convey, or propagate by procuring conveyance of, a 515 | covered work, and grant a patent license to some of the parties 516 | receiving the covered work authorizing them to use, propagate, modify 517 | or convey a specific copy of the covered work, then the patent license 518 | you grant is automatically extended to all recipients of the covered 519 | work and works based on it. 520 | 521 | A patent license is "discriminatory" if it does not include within 522 | the scope of its coverage, prohibits the exercise of, or is 523 | conditioned on the non-exercise of one or more of the rights that are 524 | specifically granted under this License. You may not convey a covered 525 | work if you are a party to an arrangement with a third party that is 526 | in the business of distributing software, under which you make payment 527 | to the third party based on the extent of your activity of conveying 528 | the work, and under which the third party grants, to any of the 529 | parties who would receive the covered work from you, a discriminatory 530 | patent license (a) in connection with copies of the covered work 531 | conveyed by you (or copies made from those copies), or (b) primarily 532 | for and in connection with specific products or compilations that 533 | contain the covered work, unless you entered into that arrangement, 534 | or that patent license was granted, prior to 28 March 2007. 535 | 536 | Nothing in this License shall be construed as excluding or limiting 537 | any implied license or other defenses to infringement that may 538 | otherwise be available to you under applicable patent law. 539 | 540 | 12. No Surrender of Others' Freedom. 541 | 542 | If conditions are imposed on you (whether by court order, agreement or 543 | otherwise) that contradict the conditions of this License, they do not 544 | excuse you from the conditions of this License. If you cannot convey a 545 | covered work so as to satisfy simultaneously your obligations under this 546 | License and any other pertinent obligations, then as a consequence you may 547 | not convey it at all. For example, if you agree to terms that obligate you 548 | to collect a royalty for further conveying from those to whom you convey 549 | the Program, the only way you could satisfy both those terms and this 550 | License would be to refrain entirely from conveying the Program. 551 | 552 | 13. Use with the GNU Affero General Public License. 553 | 554 | Notwithstanding any other provision of this License, you have 555 | permission to link or combine any covered work with a work licensed 556 | under version 3 of the GNU Affero General Public License into a single 557 | combined work, and to convey the resulting work. The terms of this 558 | License will continue to apply to the part which is the covered work, 559 | but the special requirements of the GNU Affero General Public License, 560 | section 13, concerning interaction through a network will apply to the 561 | combination as such. 562 | 563 | 14. Revised Versions of this License. 564 | 565 | The Free Software Foundation may publish revised and/or new versions of 566 | the GNU General Public License from time to time. Such new versions will 567 | be similar in spirit to the present version, but may differ in detail to 568 | address new problems or concerns. 569 | 570 | Each version is given a distinguishing version number. If the 571 | Program specifies that a certain numbered version of the GNU General 572 | Public License "or any later version" applies to it, you have the 573 | option of following the terms and conditions either of that numbered 574 | version or of any later version published by the Free Software 575 | Foundation. If the Program does not specify a version number of the 576 | GNU General Public License, you may choose any version ever published 577 | by the Free Software Foundation. 578 | 579 | If the Program specifies that a proxy can decide which future 580 | versions of the GNU General Public License can be used, that proxy's 581 | public statement of acceptance of a version permanently authorizes you 582 | to choose that version for the Program. 583 | 584 | Later license versions may give you additional or different 585 | permissions. However, no additional obligations are imposed on any 586 | author or copyright holder as a result of your choosing to follow a 587 | later version. 588 | 589 | 15. Disclaimer of Warranty. 590 | 591 | THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY 592 | APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT 593 | HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY 594 | OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, 595 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 596 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM 597 | IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF 598 | ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 599 | 600 | 16. Limitation of Liability. 601 | 602 | IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 603 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS 604 | THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY 605 | GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE 606 | USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF 607 | DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD 608 | PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), 609 | EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF 610 | SUCH DAMAGES. 611 | 612 | 17. Interpretation of Sections 15 and 16. 613 | 614 | If the disclaimer of warranty and limitation of liability provided 615 | above cannot be given local legal effect according to their terms, 616 | reviewing courts shall apply local law that most closely approximates 617 | an absolute waiver of all civil liability in connection with the 618 | Program, unless a warranty or assumption of liability accompanies a 619 | copy of the Program in return for a fee. 620 | 621 | END OF TERMS AND CONDITIONS 622 | 623 | How to Apply These Terms to Your New Programs 624 | 625 | If you develop a new program, and you want it to be of the greatest 626 | possible use to the public, the best way to achieve this is to make it 627 | free software which everyone can redistribute and change under these terms. 628 | 629 | To do so, attach the following notices to the program. It is safest 630 | to attach them to the start of each source file to most effectively 631 | state the exclusion of warranty; and each file should have at least 632 | the "copyright" line and a pointer to where the full notice is found. 633 | 634 | 635 | Copyright (C) 636 | 637 | This program is free software: you can redistribute it and/or modify 638 | it under the terms of the GNU General Public License as published by 639 | the Free Software Foundation, either version 3 of the License, or 640 | (at your option) any later version. 641 | 642 | This program is distributed in the hope that it will be useful, 643 | but WITHOUT ANY WARRANTY; without even the implied warranty of 644 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 645 | GNU General Public License for more details. 646 | 647 | You should have received a copy of the GNU General Public License 648 | along with this program. If not, see . 649 | 650 | Also add information on how to contact you by electronic and paper mail. 651 | 652 | If the program does terminal interaction, make it output a short 653 | notice like this when it starts in an interactive mode: 654 | 655 | Copyright (C) 656 | This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 657 | This is free software, and you are welcome to redistribute it 658 | under certain conditions; type `show c' for details. 659 | 660 | The hypothetical commands `show w' and `show c' should show the appropriate 661 | parts of the General Public License. Of course, your program's commands 662 | might be different; for a GUI interface, you would use an "about box". 663 | 664 | You should also get your employer (if you work as a programmer) or school, 665 | if any, to sign a "copyright disclaimer" for the program, if necessary. 666 | For more information on this, and how to apply and follow the GNU GPL, see 667 | . 668 | 669 | The GNU General Public License does not permit incorporating your program 670 | into proprietary programs. If your program is a subroutine library, you 671 | may consider it more useful to permit linking proprietary applications with 672 | the library. If this is what you want to do, use the GNU Lesser General 673 | Public License instead of this License. But first, please read 674 | . 675 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | App porting guide: https://github.com/syncloud/platform/wiki/App-porting-guide -------------------------------------------------------------------------------- /bin/initdb.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash -e 2 | 3 | DIR=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && cd .. && pwd ) 4 | 5 | if [ -z "$SNAP_COMMON" ]; then 6 | echo "SNAP_COMMON environment variable must be set" 7 | exit 1 8 | fi 9 | 10 | # shellcheck source=config/env 11 | . "${SNAP_DATA}/config/env" 12 | 13 | if [[ "$(whoami)" == "nextcloud" ]]; then 14 | ${DIR}/postgresql/bin/initdb.sh "$@" 15 | else 16 | sudo -E -H -u nextcloud ${DIR}/postgresql/bin/initdb.sh "$@" 17 | fi 18 | -------------------------------------------------------------------------------- /bin/nextcloud-config: -------------------------------------------------------------------------------- 1 | #!/bin/bash -e 2 | 3 | DIR=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && cd .. && pwd ) 4 | ${DIR}/bin/php-runner -f ${DIR}/bin/nextcloud-config.php "$@" 5 | -------------------------------------------------------------------------------- /bin/nextcloud-config.php: -------------------------------------------------------------------------------- 1 | 2) { 5 | if ($argc == 3) { 6 | $value = $argv[2]; 7 | if ($value === 'true') 8 | $value = true; 9 | if ($value === 'false') 10 | $value = false; 11 | } else 12 | $value = array_slice($argv, 2); 13 | echo("setting ".$argv[1]." = ".print_r($value, true)."\n"); 14 | \OC::$server->getConfig()->setSystemValue($argv[1], $value); 15 | } else { 16 | echo("usage: ".$argv[0]." key value1 [value2] [value3] ...\n"); 17 | exit(1); 18 | } 19 | -------------------------------------------------------------------------------- /bin/nextcloud-cron: -------------------------------------------------------------------------------- 1 | #!/bin/bash -e 2 | 3 | DIR=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && cd .. && pwd ) 4 | ${DIR}/bin/php-runner -f ${DIR}/nextcloud/cron.php 5 | -------------------------------------------------------------------------------- /bin/occ-runner: -------------------------------------------------------------------------------- 1 | #!/bin/bash -e 2 | 3 | DIR=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && cd .. && pwd ) 4 | ${DIR}/bin/php-runner ${DIR}/nextcloud/occ "$@" 5 | -------------------------------------------------------------------------------- /bin/pg_dumpall.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash -e 2 | 3 | DIR=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && cd .. && pwd ) 4 | 5 | if [ -z "$SNAP_COMMON" ]; then 6 | echo "SNAP_COMMON environment variable must be set" 7 | exit 1 8 | fi 9 | 10 | # shellcheck source=config/env 11 | . "${SNAP_DATA}/config/env" 12 | 13 | if [[ "$(whoami)" == "nextcloud" ]]; then 14 | ${DIR}/postgresql/bin/pg_dumpall.sh -p ${PSQL_PORT} -h ${PSQL_DATABASE} "$@" 15 | else 16 | sudo -E -H -u nextcloud ${DIR}/postgresql/bin/pg_dumpall.sh -p ${PSQL_PORT} -h ${PSQL_DATABASE} "$@" 17 | fi 18 | -------------------------------------------------------------------------------- /bin/php-runner: -------------------------------------------------------------------------------- 1 | #!/bin/bash -e 2 | 3 | DIR=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd ) 4 | 5 | if [[ "$(whoami)" == "nextcloud" ]]; then 6 | ${DIR}/php.sh "$@" 7 | else 8 | sudo -H -u nextcloud ${DIR}/php.sh "$@" 9 | fi 10 | -------------------------------------------------------------------------------- /bin/php.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash -e 2 | DIR=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && cd .. && pwd ) 3 | SNAP_DATA=/var/snap/nextcloud/current 4 | export LANG=en_US.UTF-8 5 | export LC_ALL=en_US.UTF-8 6 | export LC_ALL=en_US.UTF-8 7 | export LC_TIME=en_US.UTF-8 8 | export NEXTCLOUD_CONFIG_DIR=${SNAP_DATA}/nextcloud/config 9 | exec $DIR/php/bin/php.sh -c ${SNAP_DATA}/config/php.ini "$@" 10 | -------------------------------------------------------------------------------- /bin/psql.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash -e 2 | 3 | DIR=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && cd .. && pwd ) 4 | 5 | if [ -z "$SNAP_COMMON" ]; then 6 | echo "SNAP_COMMON environment variable must be set" 7 | exit 1 8 | fi 9 | 10 | # shellcheck source=config/env 11 | . "${SNAP_DATA}/config/env" 12 | 13 | if [[ "$(whoami)" == "nextcloud" ]]; then 14 | ${DIR}/postgresql/bin/psql.sh -p ${PSQL_PORT} -h ${PSQL_DATABASE} "$@" 15 | else 16 | sudo -E -H -u nextcloud ${DIR}/postgresql/bin/psql.sh -p ${PSQL_PORT} -h ${PSQL_DATABASE} "$@" 17 | fi 18 | -------------------------------------------------------------------------------- /bin/service.nginx.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash -e 2 | 3 | DIR=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && cd .. && pwd ) 4 | 5 | /bin/rm -f ${SNAP_COMMON}/web.socket 6 | /bin/rm -f ${SNAP_COMMON}/log/nginx*.log 7 | exec ${DIR}/nginx/bin/nginx.sh -c ${SNAP_DATA}/config/nginx.conf -p ${DIR}/nginx -e stderr 8 | -------------------------------------------------------------------------------- /bin/service.php-fpm.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | DIR=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && cd .. && pwd ) 4 | 5 | if [[ -z "$1" ]]; then 6 | echo "usage $0 [start]" 7 | exit 1 8 | fi 9 | 10 | case $1 in 11 | start) 12 | exec $DIR/php/bin/php-fpm.sh -y ${SNAP_DATA}/config/php-fpm.conf -c ${SNAP_DATA}/config/php.ini 13 | ;; 14 | post-start) 15 | timeout 5 /bin/bash -c 'until [ -S '${SNAP_COMMON}'/log/php5-fpm.sock ]; do echo "waiting for ${SNAP_COMMON}/log/php5-fpm.sock"; sleep 1; done' 16 | ;; 17 | *) 18 | echo "not valid command" 19 | exit 1 20 | ;; 21 | esac 22 | -------------------------------------------------------------------------------- /bin/service.postgresql.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash -e 2 | 3 | DIR=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && cd .. && pwd ) 4 | 5 | if [[ -z "$1" ]]; then 6 | echo "usage $0 [start]" 7 | exit 1 8 | fi 9 | # shellcheck source=config/env 10 | . "${SNAP_DATA}/config/env" 11 | 12 | case $1 in 13 | start) 14 | exec ${DIR}/postgresql/bin/pg_ctl.sh -w -s -D ${PSQL_DATABASE} start 15 | ;; 16 | reload) 17 | exec ${DIR}/postgresql/bin/pg_ctl.sh -s -D ${PSQL_DATABASE} reload 18 | ;; 19 | stop) 20 | exec ${DIR}/postgresql/bin/pg_ctl.sh -s -D ${PSQL_DATABASE} stop -m fast 21 | ;; 22 | *) 23 | echo "not valid command" 24 | exit 1 25 | ;; 26 | esac 27 | -------------------------------------------------------------------------------- /bin/service.redis.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | DIR=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && cd .. && pwd ) 3 | exec $DIR/redis/bin/redis.sh ${SNAP_DATA}/config/redis.conf -------------------------------------------------------------------------------- /build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash -ex 2 | 3 | DIR=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd ) 4 | cd ${DIR} 5 | 6 | BUILD_DIR=${DIR}/build/snap 7 | 8 | cp -r bin ${BUILD_DIR} 9 | cp -r config ${BUILD_DIR} 10 | cp -r hooks ${BUILD_DIR} 11 | rm -rf ${BUILD_DIR}/nextcloud/config 12 | ls -la ${BUILD_DIR}/nextcloud/apps 13 | 14 | #disable internal updates as they break us 15 | rm -r ${BUILD_DIR}/nextcloud/apps/updatenotification 16 | cat ${BUILD_DIR}/nextcloud/.user.ini 17 | sed -i 's/upload_max_filesize=.*/upload_max_filesize=10G/g' ${BUILD_DIR}/nextcloud/.user.ini 18 | sed -i 's/post_max_size=.*/post_max_size=10G/g' ${BUILD_DIR}/nextcloud/.user.ini 19 | ln -s /var/snap/nextcloud/current/extra-apps ${BUILD_DIR}/nextcloud/extra-apps 20 | -------------------------------------------------------------------------------- /config/config.php: -------------------------------------------------------------------------------- 1 | '{{ common_dir }}', 4 | 'check_data_directory_permissions' => false, 5 | 'log_type' => 'syslog', 6 | 'logfile' => '', 7 | 'apps_paths' => array( 8 | array( 9 | 'path'=> '{{ app_dir }}/nextcloud/apps', 10 | 'url' => '/apps', 11 | 'writable' => false, 12 | ), 13 | array( 14 | 'path'=> '{{ app_dir }}/nextcloud/extra-apps', 15 | 'url' => '/extra-apps', 16 | 'writable' => true, 17 | ), 18 | ), 19 | 'enable_previews' => true, 20 | 'enabledPreviewProviders' => 21 | array ( 22 | 'OC\Preview\Movie', 23 | 'OC\Preview\PNG', 24 | 'OC\Preview\JPEG', 25 | 'OC\Preview\GIF', 26 | 'OC\Preview\BMP', 27 | 'OC\Preview\XBitmap', 28 | 'OC\Preview\MP3', 29 | 'OC\Preview\MP4', 30 | 'OC\Preview\TXT', 31 | 'OC\Preview\MarkDown', 32 | 'OC\Preview\PDF' 33 | ), 34 | 'bulkupload.enabled' => false, 35 | 'memcache.local' => '\OC\Memcache\APCu', 36 | 'memcache.distributed' => '\OC\Memcache\Redis', 37 | 'memcache.locking' => '\OC\Memcache\Redis', 38 | 'redis' => [ 39 | 'host' => '/var/snap/nextcloud/current/redis.sock', 40 | 'port' => 0, 41 | ], 42 | 'maintenance_window_start' => 1, 43 | 'allow_local_remote_servers' => true, 44 | ); 45 | -------------------------------------------------------------------------------- /config/env: -------------------------------------------------------------------------------- 1 | export PSQL_PORT={{ db_psql_port }} 2 | export PSQL_DATABASE={{ database_dir }} 3 | -------------------------------------------------------------------------------- /config/fastcgi_params: -------------------------------------------------------------------------------- 1 | 2 | fastcgi_param QUERY_STRING $query_string; 3 | fastcgi_param REQUEST_METHOD $request_method; 4 | fastcgi_param CONTENT_TYPE $content_type; 5 | fastcgi_param CONTENT_LENGTH $content_length; 6 | 7 | fastcgi_param SCRIPT_NAME $fastcgi_script_name; 8 | fastcgi_param REQUEST_URI $request_uri; 9 | fastcgi_param DOCUMENT_URI $document_uri; 10 | fastcgi_param DOCUMENT_ROOT $document_root; 11 | fastcgi_param SERVER_PROTOCOL $server_protocol; 12 | fastcgi_param HTTPS $https if_not_empty; 13 | 14 | fastcgi_param GATEWAY_INTERFACE CGI/1.1; 15 | fastcgi_param SERVER_SOFTWARE nginx/$nginx_version; 16 | 17 | fastcgi_param REMOTE_ADDR $remote_addr; 18 | fastcgi_param REMOTE_PORT $remote_port; 19 | fastcgi_param SERVER_ADDR $server_addr; 20 | fastcgi_param SERVER_PORT $server_port; 21 | fastcgi_param SERVER_NAME $server_name; 22 | 23 | # PHP only, required if PHP was built with --enable-force-cgi-redirect 24 | fastcgi_param REDIRECT_STATUS 200; 25 | -------------------------------------------------------------------------------- /config/nginx.conf: -------------------------------------------------------------------------------- 1 | # Version 2024-07-17 2 | # https://github.com/nextcloud/documentation/blob/master/admin_manual/installation/nginx-root.conf.sample 3 | worker_processes 4; 4 | 5 | error_log syslog:server=unix:/dev/log warn; 6 | pid /var/snap/nextcloud/common/log/nginx.pid; 7 | daemon off; 8 | 9 | events { 10 | worker_connections 1024; 11 | } 12 | 13 | http { 14 | access_log syslog:server=unix:/dev/log; 15 | 16 | upstream php-handler { 17 | server unix:/var/snap/nextcloud/common/log/php5-fpm.sock; 18 | } 19 | 20 | # Set the `immutable` cache control options only for assets with a cache busting `v` argument 21 | map $arg_v $asset_immutable { 22 | "" ""; 23 | default ", immutable"; 24 | } 25 | 26 | client_body_temp_path /var/snap/nextcloud/common/nginx/client_body_temp; 27 | proxy_temp_path /var/snap/nextcloud/common/nginx/proxy_temp; 28 | fastcgi_temp_path /var/snap/nextcloud/common/nginx/fastcgi_temp; 29 | uwsgi_temp_path /var/snap/nextcloud/common/nginx/puwsgi_temp; 30 | scgi_temp_path /var/snap/nextcloud/common/nginx/scgi_temp; 31 | 32 | # solves app update issue 33 | absolute_redirect off; 34 | 35 | server { 36 | listen unix:/var/snap/nextcloud/common/web.socket; 37 | set_real_ip_from unix:; 38 | server_name localhost; 39 | 40 | # Path to the root of your installation 41 | root /snap/nextcloud/current/nextcloud; 42 | 43 | # Prevent nginx HTTP Server Detection 44 | server_tokens off; 45 | 46 | # HSTS settings 47 | # WARNING: Only add the preload option once you read about 48 | # the consequences in https://hstspreload.org/. This option 49 | # will add the domain to a hardcoded list that is shipped 50 | # in all major browsers and getting removed from this list 51 | # could take several months. 52 | #add_header Strict-Transport-Security "max-age=15768000; includeSubDomains; preload;" always; 53 | 54 | # set max upload size and increase upload timeout: 55 | client_max_body_size 10G; 56 | client_body_timeout 300s; 57 | fastcgi_buffers 64 4K; 58 | fastcgi_read_timeout 600s; 59 | fastcgi_send_timeout 600s; 60 | 61 | # Enable gzip but do not remove ETag headers 62 | gzip on; 63 | gzip_vary on; 64 | gzip_comp_level 4; 65 | gzip_min_length 256; 66 | gzip_proxied expired no-cache no-store private no_last_modified no_etag auth; 67 | gzip_types application/atom+xml text/javascript application/javascript application/json application/ld+json application/manifest+json application/rss+xml application/vnd.geo+json application/vnd.ms-fontobject application/wasm application/x-font-ttf application/x-web-app-manifest+json application/xhtml+xml application/xml font/opentype image/bmp image/svg+xml image/x-icon text/cache-manifest text/css text/plain text/vcard text/vnd.rim.location.xloc text/vtt text/x-component text/x-cross-domain-policy; 68 | 69 | # Pagespeed is not supported by Nextcloud, so if your server is built 70 | # with the `ngx_pagespeed` module, uncomment this line to disable it. 71 | #pagespeed off; 72 | 73 | # The settings allows you to optimize the HTTP2 bandwidth. 74 | # See https://blog.cloudflare.com/delivering-http-2-upload-speed-improvements/ 75 | # for tuning hints 76 | client_body_buffer_size 512k; 77 | 78 | # HTTP response headers borrowed from Nextcloud `.htaccess` 79 | add_header Referrer-Policy "no-referrer" always; 80 | add_header X-Content-Type-Options "nosniff" always; 81 | add_header X-Frame-Options "SAMEORIGIN" always; 82 | add_header X-Permitted-Cross-Domain-Policies "none" always; 83 | add_header X-Robots-Tag "noindex, nofollow" always; 84 | add_header X-XSS-Protection "1; mode=block" always; 85 | 86 | # Remove X-Powered-By, which is an information leak 87 | fastcgi_hide_header X-Powered-By; 88 | 89 | # Set .mjs and .wasm MIME types 90 | # Either include it in the default mime.types list 91 | # and include that list explicitly or add the file extension 92 | # only for Nextcloud like below: 93 | include /snap/nextcloud/current/nginx/etc/nginx/mime.types; 94 | types { 95 | text/javascript js mjs; 96 | application/wasm wasm; 97 | } 98 | 99 | # Specify how to handle directories -- specifying `/index.php$request_uri` 100 | # here as the fallback means that Nginx always exhibits the desired behaviour 101 | # when a client requests a path that corresponds to a directory that exists 102 | # on the server. In particular, if that directory contains an index.php file, 103 | # that file is correctly served; if it doesn't, then the request is passed to 104 | # the front-end controller. This consistent behaviour means that we don't need 105 | # to specify custom rules for certain paths (e.g. images and other assets, 106 | # `/updater`, `/ocs-provider`), and thus 107 | # `try_files $uri $uri/ /index.php$request_uri` 108 | # always provides the desired behaviour. 109 | index index.php index.html /index.php$request_uri; 110 | 111 | # Rule borrowed from `.htaccess` to handle Microsoft DAV clients 112 | location = / { 113 | if ( $http_user_agent ~ ^DavClnt ) { 114 | return 302 /remote.php/webdav/$is_args$args; 115 | } 116 | } 117 | 118 | location = /robots.txt { 119 | allow all; 120 | log_not_found off; 121 | access_log off; 122 | } 123 | 124 | # Make a regex exception for `/.well-known` so that clients can still 125 | # access it despite the existence of the regex rule 126 | # `location ~ /(\.|autotest|...)` which would otherwise handle requests 127 | # for `/.well-known`. 128 | location ^~ /.well-known { 129 | # The rules in this block are an adaptation of the rules 130 | # in `.htaccess` that concern `/.well-known`. 131 | 132 | location = /.well-known/carddav { return 301 /remote.php/dav/; } 133 | location = /.well-known/caldav { return 301 /remote.php/dav/; } 134 | 135 | location /.well-known/acme-challenge { try_files $uri $uri/ =404; } 136 | location /.well-known/pki-validation { try_files $uri $uri/ =404; } 137 | 138 | # Let Nextcloud's API for `/.well-known` URIs handle all other 139 | # requests by passing them to the front-end controller. 140 | return 301 /index.php$request_uri; 141 | } 142 | 143 | # Rules borrowed from `.htaccess` to hide certain paths from clients 144 | location ~ ^/(?:build|tests|config|lib|3rdparty|templates|data)(?:$|/) { return 404; } 145 | location ~ ^/(?:\.|autotest|occ|issue|indie|db_|console) { return 404; } 146 | 147 | # Ensure this block, which passes PHP files to the PHP process, is above the blocks 148 | # which handle static assets (as seen below). If this block is not declared first, 149 | # then Nginx will encounter an infinite rewriting loop when it prepends `/index.php` 150 | # to the URI, resulting in a HTTP 500 error response. 151 | location ~ \.php(?:$|/) { 152 | # Required for legacy support 153 | rewrite ^/(?!index|remote|public|cron|core\/ajax\/update|status|ocs\/v[12]|updater\/.+|ocs-provider\/.+|.+\/richdocumentscode(_arm64)?\/proxy) /index.php$request_uri; 154 | 155 | fastcgi_split_path_info ^(.+?\.php)(/.*)$; 156 | set $path_info $fastcgi_path_info; 157 | 158 | try_files $fastcgi_script_name =404; 159 | 160 | include fastcgi_params; 161 | fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; 162 | fastcgi_param PATH_INFO $path_info; 163 | fastcgi_param HTTPS on; 164 | 165 | fastcgi_param modHeadersAvailable true; # Avoid sending the security headers twice 166 | fastcgi_param front_controller_active true; # Enable pretty urls 167 | fastcgi_pass php-handler; 168 | 169 | fastcgi_intercept_errors on; 170 | fastcgi_request_buffering off; 171 | fastcgi_param REMOTE_ADDR $remote_addr; 172 | 173 | fastcgi_max_temp_file_size 0; 174 | } 175 | 176 | # Serve static files 177 | location ~ \.(?:css|js|mjs|svg|gif|ico|jpg|png|webp|wasm|tflite|map|ogg|flac)$ { 178 | try_files $uri /index.php$request_uri; 179 | # HTTP response headers borrowed from Nextcloud `.htaccess` 180 | add_header Cache-Control "public, max-age=15778463$asset_immutable"; 181 | add_header Referrer-Policy "no-referrer" always; 182 | add_header X-Content-Type-Options "nosniff" always; 183 | add_header X-Frame-Options "SAMEORIGIN" always; 184 | add_header X-Permitted-Cross-Domain-Policies "none" always; 185 | add_header X-Robots-Tag "noindex, nofollow" always; 186 | add_header X-XSS-Protection "1; mode=block" always; 187 | access_log off; # Optional: Don't log access to assets 188 | } 189 | 190 | location ~ \.(otf|woff2?)$ { 191 | try_files $uri /index.php$request_uri; 192 | expires 7d; # Cache-Control policy borrowed from `.htaccess` 193 | access_log off; # Optional: Don't log access to assets 194 | } 195 | 196 | # Rule borrowed from `.htaccess` 197 | location /remote { 198 | return 301 /remote.php$request_uri; 199 | } 200 | 201 | location / { 202 | try_files $uri $uri/ /index.php$request_uri; 203 | } 204 | } 205 | } -------------------------------------------------------------------------------- /config/php-fpm.conf: -------------------------------------------------------------------------------- 1 | ;;;;;;;;;;;;;;;;;;;;; 2 | ; FPM Configuration ; 3 | ;;;;;;;;;;;;;;;;;;;;; 4 | 5 | ; All relative paths in this configuration file are relative to PHP's install 6 | ; prefix (/opt/syncloud-nextcloud/php). This prefix can be dynamically changed by using the 7 | ; '-p' argument from the command line. 8 | 9 | ; Include one or more files. If glob(3) exists, it is used to include a bunch of 10 | ; files from a glob(3) pattern. This directive can be used everywhere in the 11 | ; file. 12 | ; Relative path can also be used. They will be prefixed by: 13 | ; - the global prefix if it's been set (-p argument) 14 | ; - /opt/syncloud-nextcloud/php otherwise 15 | ;include=etc/fpm.d/*.conf 16 | 17 | ;;;;;;;;;;;;;;;;;; 18 | ; Global Options ; 19 | ;;;;;;;;;;;;;;;;;; 20 | 21 | [global] 22 | ; Pid file 23 | ; Note: the default prefix is /opt/syncloud-nextcloud/php/var 24 | ; Default Value: none 25 | pid = {{ common_dir }}/php-fpm.pid 26 | 27 | ; Error log file 28 | ; If it's set to "syslog", log is sent to syslogd instead of being written 29 | ; in a local file. 30 | ; Note: the default prefix is /opt/syncloud-nextcloud/php/var 31 | ; Default Value: log/php-fpm.log 32 | error_log = syslog 33 | 34 | ; syslog_facility is used to specify what type of program is logging the 35 | ; message. This lets syslogd specify that messages from different facilities 36 | ; will be handled differently. 37 | ; See syslog(3) for possible values (ex daemon equiv LOG_DAEMON) 38 | ; Default Value: daemon 39 | ;syslog.facility = daemon 40 | 41 | ; syslog_ident is prepended to every message. If you have multiple FPM 42 | ; instances running on the same server, you can change the default value 43 | ; which must suit common needs. 44 | ; Default Value: php-fpm 45 | syslog.ident = nextcloud.php-fpm 46 | 47 | ; Log level 48 | ; Possible Values: alert, error, warning, notice, debug 49 | ; Default Value: notice 50 | ;log_level = notice 51 | 52 | ; If this number of child processes exit with SIGSEGV or SIGBUS within the time 53 | ; interval set by emergency_restart_interval then FPM will restart. A value 54 | ; of '0' means 'Off'. 55 | ; Default Value: 0 56 | ;emergency_restart_threshold = 0 57 | 58 | ; Interval of time used by emergency_restart_interval to determine when 59 | ; a graceful restart will be initiated. This can be useful to work around 60 | ; accidental corruptions in an accelerator's shared memory. 61 | ; Available Units: s(econds), m(inutes), h(ours), or d(ays) 62 | ; Default Unit: seconds 63 | ; Default Value: 0 64 | ;emergency_restart_interval = 0 65 | 66 | ; Time limit for child processes to wait for a reaction on signals from master. 67 | ; Available units: s(econds), m(inutes), h(ours), or d(ays) 68 | ; Default Unit: seconds 69 | ; Default Value: 0 70 | ;process_control_timeout = 0 71 | 72 | ; The maximum number of processes FPM will fork. This has been design to control 73 | ; the global number of processes when using dynamic PM within a lot of pools. 74 | ; Use it with caution. 75 | ; Note: A value of 0 indicates no limit 76 | ; Default Value: 0 77 | ; process.max = 128 78 | 79 | ; Specify the nice(2) priority to apply to the master process (only if set) 80 | ; The value can vary from -19 (highest priority) to 20 (lower priority) 81 | ; Note: - It will only work if the FPM master process is launched as root 82 | ; - The pool process will inherit the master process priority 83 | ; unless it specified otherwise 84 | ; Default Value: no set 85 | ; process.priority = -19 86 | 87 | ; Send FPM to background. Set to 'no' to keep FPM in foreground for debugging. 88 | ; Default Value: yes 89 | ;daemonize = yes 90 | 91 | ; Set open file descriptor rlimit for the master process. 92 | ; Default Value: system defined value 93 | ;rlimit_files = 1024 94 | 95 | ; Set max core size rlimit for the master process. 96 | ; Possible Values: 'unlimited' or an integer greater or equal to 0 97 | ; Default Value: system defined value 98 | ;rlimit_core = 0 99 | 100 | ; Specify the event mechanism FPM will use. The following is available: 101 | ; - select (any POSIX os) 102 | ; - poll (any POSIX os) 103 | ; - epoll (linux >= 2.5.44) 104 | ; - kqueue (FreeBSD >= 4.1, OpenBSD >= 2.9, NetBSD >= 2.0) 105 | ; - /dev/poll (Solaris >= 7) 106 | ; - port (Solaris >= 10) 107 | ; Default Value: not set (auto detection) 108 | ;events.mechanism = epoll 109 | 110 | ; When FPM is build with systemd integration, specify the interval, 111 | ; in second, between health report notification to systemd. 112 | ; Set to 0 to disable. 113 | ; Available Units: s(econds), m(inutes), h(ours) 114 | ; Default Unit: seconds 115 | ; Default value: 10 116 | ;systemd_interval = 10 117 | 118 | ;;;;;;;;;;;;;;;;;;;; 119 | ; Pool Definitions ; 120 | ;;;;;;;;;;;;;;;;;;;; 121 | 122 | ; Multiple pools of child processes may be started with different listening 123 | ; ports and different management options. The name of the pool will be 124 | ; used in logs and stats. There is no limitation on the number of pools which 125 | ; FPM can handle. Your system will tell you anyway :) 126 | 127 | ; Start a new pool named 'www'. 128 | ; the variable $pool can we used in any directive and will be replaced by the 129 | ; pool name ('www' here) 130 | [www] 131 | 132 | ; Per pool prefix 133 | ; It only applies on the following directives: 134 | ; - 'access.log' 135 | ; - 'slowlog' 136 | ; - 'listen' (unixsocket) 137 | ; - 'chroot' 138 | ; - 'chdir' 139 | ; - 'php_values' 140 | ; - 'php_admin_values' 141 | ; When not set, the global prefix (or /opt/syncloud-nextcloud/php) applies instead. 142 | ; Note: This directive can also be relative to the global prefix. 143 | ; Default Value: none 144 | ;prefix = /path/to/pools/$pool 145 | 146 | ; Unix user/group of processes 147 | ; Note: The user is mandatory. If the group is not set, the default user's group 148 | ; will be used. 149 | user = nextcloud 150 | group = nextcloud 151 | 152 | ; The address on which to accept FastCGI requests. 153 | ; Valid syntaxes are: 154 | ; 'ip.add.re.ss:port' - to listen on a TCP socket to a specific IPv4 address on 155 | ; a specific port; 156 | ; '[ip:6:addr:ess]:port' - to listen on a TCP socket to a specific IPv6 address on 157 | ; a specific port; 158 | ; 'port' - to listen on a TCP socket to all IPv4 addresses on a 159 | ; specific port; 160 | ; '[::]:port' - to listen on a TCP socket to all addresses 161 | ; (IPv6 and IPv4-mapped) on a specific port; 162 | ; '/path/to/unix/socket' - to listen on a unix socket. 163 | ; Note: This value is mandatory. 164 | listen = {{ common_dir }}/log/php5-fpm.sock 165 | 166 | ; Set listen(2) backlog. 167 | ; Default Value: 65535 (-1 on FreeBSD and OpenBSD) 168 | ;listen.backlog = 65535 169 | 170 | ; Set permissions for unix socket, if one is used. In Linux, read/write 171 | ; permissions must be set in order to allow connections from a web server. Many 172 | ; BSD-derived systems allow connections regardless of permissions. 173 | ; Default Values: user and group are set as the running user 174 | ; mode is set to 0660 175 | ;listen.owner = nobody 176 | ;listen.group = nobody 177 | ;listen.mode = 0660 178 | ; When POSIX Access Control Lists are supported you can set them using 179 | ; these options, value is a comma separated list of user/group names. 180 | ; When set, listen.owner and listen.group are ignored 181 | ;listen.acl_users = 182 | ;listen.acl_groups = 183 | 184 | ; List of addresses (IPv4/IPv6) of FastCGI clients which are allowed to connect. 185 | ; Equivalent to the FCGI_WEB_SERVER_ADDRS environment variable in the original 186 | ; PHP FCGI (5.2.2+). Makes sense only with a tcp listening socket. Each address 187 | ; must be separated by a comma. If this value is left blank, connections will be 188 | ; accepted from any ip address. 189 | ; Default Value: any 190 | ;listen.allowed_clients = 127.0.0.1 191 | 192 | ; Specify the nice(2) priority to apply to the pool processes (only if set) 193 | ; The value can vary from -19 (highest priority) to 20 (lower priority) 194 | ; Note: - It will only work if the FPM master process is launched as root 195 | ; - The pool processes will inherit the master process priority 196 | ; unless it specified otherwise 197 | ; Default Value: no set 198 | ; process.priority = -19 199 | 200 | ; Choose how the process manager will control the number of child processes. 201 | ; Possible Values: 202 | ; static - a fixed number (pm.max_children) of child processes; 203 | ; dynamic - the number of child processes are set dynamically based on the 204 | ; following directives. With this process management, there will be 205 | ; always at least 1 children. 206 | ; pm.max_children - the maximum number of children that can 207 | ; be alive at the same time. 208 | ; pm.start_servers - the number of children created on startup. 209 | ; pm.min_spare_servers - the minimum number of children in 'idle' 210 | ; state (waiting to process). If the number 211 | ; of 'idle' processes is less than this 212 | ; number then some children will be created. 213 | ; pm.max_spare_servers - the maximum number of children in 'idle' 214 | ; state (waiting to process). If the number 215 | ; of 'idle' processes is greater than this 216 | ; number then some children will be killed. 217 | ; ondemand - no children are created at startup. Children will be forked when 218 | ; new requests will connect. The following parameter are used: 219 | ; pm.max_children - the maximum number of children that 220 | ; can be alive at the same time. 221 | ; pm.process_idle_timeout - The number of seconds after which 222 | ; an idle process will be killed. 223 | ; Note: This value is mandatory. 224 | pm = dynamic 225 | 226 | ; The number of child processes to be created when pm is set to 'static' and the 227 | ; maximum number of child processes when pm is set to 'dynamic' or 'ondemand'. 228 | ; This value sets the limit on the number of simultaneous requests that will be 229 | ; served. Equivalent to the ApacheMaxClients directive with mpm_prefork. 230 | ; Equivalent to the PHP_FCGI_CHILDREN environment variable in the original PHP 231 | ; CGI. The below defaults are based on a server without much resources. Don't 232 | ; forget to tweak pm.* to fit your needs. 233 | ; Note: Used when pm is set to 'static', 'dynamic' or 'ondemand' 234 | ; Note: This value is mandatory. 235 | pm.max_children = 10 236 | 237 | ; The number of child processes created on startup. 238 | ; Note: Used only when pm is set to 'dynamic' 239 | ; Default Value: min_spare_servers + (max_spare_servers - min_spare_servers) / 2 240 | pm.start_servers = 2 241 | 242 | ; The desired minimum number of idle server processes. 243 | ; Note: Used only when pm is set to 'dynamic' 244 | ; Note: Mandatory when pm is set to 'dynamic' 245 | pm.min_spare_servers = 1 246 | 247 | ; The desired maximum number of idle server processes. 248 | ; Note: Used only when pm is set to 'dynamic' 249 | ; Note: Mandatory when pm is set to 'dynamic' 250 | pm.max_spare_servers = 3 251 | 252 | ; The number of seconds after which an idle process will be killed. 253 | ; Note: Used only when pm is set to 'ondemand' 254 | ; Default Value: 10s 255 | ;pm.process_idle_timeout = 10s; 256 | 257 | ; The number of requests each child process should execute before respawning. 258 | ; This can be useful to work around memory leaks in 3rd party libraries. For 259 | ; endless request processing specify '0'. Equivalent to PHP_FCGI_MAX_REQUESTS. 260 | ; Default Value: 0 261 | ;pm.max_requests = 500 262 | 263 | ; The URI to view the FPM status page. If this value is not set, no URI will be 264 | ; recognized as a status page. It shows the following informations: 265 | ; pool - the name of the pool; 266 | ; process manager - static, dynamic or ondemand; 267 | ; start time - the date and time FPM has started; 268 | ; start since - number of seconds since FPM has started; 269 | ; accepted conn - the number of request accepted by the pool; 270 | ; listen queue - the number of request in the queue of pending 271 | ; connections (see backlog in listen(2)); 272 | ; max listen queue - the maximum number of requests in the queue 273 | ; of pending connections since FPM has started; 274 | ; listen queue len - the size of the socket queue of pending connections; 275 | ; idle processes - the number of idle processes; 276 | ; active processes - the number of active processes; 277 | ; total processes - the number of idle + active processes; 278 | ; max active processes - the maximum number of active processes since FPM 279 | ; has started; 280 | ; max children reached - number of times, the process limit has been reached, 281 | ; when pm tries to start more children (works only for 282 | ; pm 'dynamic' and 'ondemand'); 283 | ; Value are updated in real time. 284 | ; Example output: 285 | ; pool: www 286 | ; process manager: static 287 | ; start time: 01/Jul/2011:17:53:49 +0200 288 | ; start since: 62636 289 | ; accepted conn: 190460 290 | ; listen queue: 0 291 | ; max listen queue: 1 292 | ; listen queue len: 42 293 | ; idle processes: 4 294 | ; active processes: 11 295 | ; total processes: 15 296 | ; max active processes: 12 297 | ; max children reached: 0 298 | ; 299 | ; By default the status page output is formatted as text/plain. Passing either 300 | ; 'html', 'xml' or 'json' in the query string will return the corresponding 301 | ; output syntax. Example: 302 | ; http://www.foo.bar/status 303 | ; http://www.foo.bar/status?json 304 | ; http://www.foo.bar/status?html 305 | ; http://www.foo.bar/status?xml 306 | ; 307 | ; By default the status page only outputs short status. Passing 'full' in the 308 | ; query string will also return status for each pool process. 309 | ; Example: 310 | ; http://www.foo.bar/status?full 311 | ; http://www.foo.bar/status?json&full 312 | ; http://www.foo.bar/status?html&full 313 | ; http://www.foo.bar/status?xml&full 314 | ; The Full status returns for each process: 315 | ; pid - the PID of the process; 316 | ; state - the state of the process (Idle, Running, ...); 317 | ; start time - the date and time the process has started; 318 | ; start since - the number of seconds since the process has started; 319 | ; requests - the number of requests the process has served; 320 | ; request duration - the duration in µs of the requests; 321 | ; request method - the request method (GET, POST, ...); 322 | ; request URI - the request URI with the query string; 323 | ; content length - the content length of the request (only with POST); 324 | ; user - the user (PHP_AUTH_USER) (or '-' if not set); 325 | ; script - the main script called (or '-' if not set); 326 | ; last request cpu - the %cpu the last request consumed 327 | ; it's always 0 if the process is not in Idle state 328 | ; because CPU calculation is done when the request 329 | ; processing has terminated; 330 | ; last request memory - the max amount of memory the last request consumed 331 | ; it's always 0 if the process is not in Idle state 332 | ; because memory calculation is done when the request 333 | ; processing has terminated; 334 | ; If the process is in Idle state, then informations are related to the 335 | ; last request the process has served. Otherwise informations are related to 336 | ; the current request being served. 337 | ; Example output: 338 | ; ************************ 339 | ; pid: 31330 340 | ; state: Running 341 | ; start time: 01/Jul/2011:17:53:49 +0200 342 | ; start since: 63087 343 | ; requests: 12808 344 | ; request duration: 1250261 345 | ; request method: GET 346 | ; request URI: /test_mem.php?N=10000 347 | ; content length: 0 348 | ; user: - 349 | ; script: /home/fat/web/docs/php/test_mem.php 350 | ; last request cpu: 0.00 351 | ; last request memory: 0 352 | ; 353 | ; Note: There is a real-time FPM status monitoring sample web page available 354 | ; It's available in: /opt/syncloud-nextcloud/php/share/php/fpm/status.html 355 | ; 356 | ; Note: The value must start with a leading slash (/). The value can be 357 | ; anything, but it may not be a good idea to use the .php extension or it 358 | ; may conflict with a real PHP file. 359 | ; Default Value: not set 360 | ;pm.status_path = /status 361 | 362 | ; The ping URI to call the monitoring page of FPM. If this value is not set, no 363 | ; URI will be recognized as a ping page. This could be used to test from outside 364 | ; that FPM is alive and responding, or to 365 | ; - create a graph of FPM availability (rrd or such); 366 | ; - remove a server from a group if it is not responding (load balancing); 367 | ; - trigger alerts for the operating team (24/7). 368 | ; Note: The value must start with a leading slash (/). The value can be 369 | ; anything, but it may not be a good idea to use the .php extension or it 370 | ; may conflict with a real PHP file. 371 | ; Default Value: not set 372 | ;ping.path = /ping 373 | 374 | ; This directive may be used to customize the response of a ping request. The 375 | ; response is formatted as text/plain with a 200 response code. 376 | ; Default Value: pong 377 | ;ping.response = pong 378 | 379 | ; The access log file 380 | ; Default: not set 381 | ;access.log = log/$pool.access.log 382 | 383 | ; The access log format. 384 | ; The following syntax is allowed 385 | ; %%: the '%' character 386 | ; %C: %CPU used by the request 387 | ; it can accept the following format: 388 | ; - %{user}C for user CPU only 389 | ; - %{system}C for system CPU only 390 | ; - %{total}C for user + system CPU (default) 391 | ; %d: time taken to serve the request 392 | ; it can accept the following format: 393 | ; - %{seconds}d (default) 394 | ; - %{miliseconds}d 395 | ; - %{mili}d 396 | ; - %{microseconds}d 397 | ; - %{micro}d 398 | ; %e: an environment variable (same as $_ENV or $_SERVER) 399 | ; it must be associated with embraces to specify the name of the env 400 | ; variable. Some exemples: 401 | ; - server specifics like: %{REQUEST_METHOD}e or %{SERVER_PROTOCOL}e 402 | ; - HTTP headers like: %{HTTP_HOST}e or %{HTTP_USER_AGENT}e 403 | ; %f: script filename 404 | ; %l: content-length of the request (for POST request only) 405 | ; %m: request method 406 | ; %M: peak of memory allocated by PHP 407 | ; it can accept the following format: 408 | ; - %{bytes}M (default) 409 | ; - %{kilobytes}M 410 | ; - %{kilo}M 411 | ; - %{megabytes}M 412 | ; - %{mega}M 413 | ; %n: pool name 414 | ; %o: output header 415 | ; it must be associated with embraces to specify the name of the header: 416 | ; - %{Content-Type}o 417 | ; - %{X-Powered-By}o 418 | ; - %{Transfert-Encoding}o 419 | ; - .... 420 | ; %p: PID of the child that serviced the request 421 | ; %P: PID of the parent of the child that serviced the request 422 | ; %q: the query string 423 | ; %Q: the '?' character if query string exists 424 | ; %r: the request URI (without the query string, see %q and %Q) 425 | ; %R: remote IP address 426 | ; %s: status (response code) 427 | ; %t: server time the request was received 428 | ; it can accept a strftime(3) format: 429 | ; %d/%b/%Y:%H:%M:%S %z (default) 430 | ; %T: time the log has been written (the request has finished) 431 | ; it can accept a strftime(3) format: 432 | ; %d/%b/%Y:%H:%M:%S %z (default) 433 | ; %u: remote user 434 | ; 435 | ; Default: "%R - %u %t \"%m %r\" %s" 436 | ;access.format = "%R - %u %t \"%m %r%Q%q\" %s %f %{mili}d %{kilo}M %C%%" 437 | 438 | ; The log file for slow requests 439 | ; Default Value: not set 440 | ; Note: slowlog is mandatory if request_slowlog_timeout is set 441 | ;slowlog = log/$pool.log.slow 442 | 443 | ; The timeout for serving a single request after which a PHP backtrace will be 444 | ; dumped to the 'slowlog' file. A value of '0s' means 'off'. 445 | ; Available units: s(econds)(default), m(inutes), h(ours), or d(ays) 446 | ; Default Value: 0 447 | ;request_slowlog_timeout = 0 448 | 449 | ; The timeout for serving a single request after which the worker process will 450 | ; be killed. This option should be used when the 'max_execution_time' ini option 451 | ; does not stop script execution for some reason. A value of '0' means 'off'. 452 | ; Available units: s(econds)(default), m(inutes), h(ours), or d(ays) 453 | ; Default Value: 0 454 | ;request_terminate_timeout = 0 455 | 456 | ; Set open file descriptor rlimit. 457 | ; Default Value: system defined value 458 | ;rlimit_files = 1024 459 | 460 | ; Set max core size rlimit. 461 | ; Possible Values: 'unlimited' or an integer greater or equal to 0 462 | ; Default Value: system defined value 463 | ;rlimit_core = 0 464 | 465 | ; Chroot to this directory at the start. This value must be defined as an 466 | ; absolute path. When this value is not set, chroot is not used. 467 | ; Note: you can prefix with '$prefix' to chroot to the pool prefix or one 468 | ; of its subdirectories. If the pool prefix is not set, the global prefix 469 | ; will be used instead. 470 | ; Note: chrooting is a great security feature and should be used whenever 471 | ; possible. However, all PHP paths will be relative to the chroot 472 | ; (error_log, sessions.save_path, ...). 473 | ; Default Value: not set 474 | ;chroot = 475 | 476 | ; Chdir to this directory at the start. 477 | ; Note: relative path can be used. 478 | ; Default Value: current directory or / when chroot 479 | ;chdir = /var/www 480 | 481 | ; Redirect worker stdout and stderr into main error log. If not set, stdout and 482 | ; stderr will be redirected to /dev/null according to FastCGI specs. 483 | ; Note: on highloaded environement, this can cause some delay in the page 484 | ; process time (several ms). 485 | ; Default Value: no 486 | catch_workers_output = yes 487 | 488 | ; Clear environment in FPM workers 489 | ; Prevents arbitrary environment variables from reaching FPM worker processes 490 | ; by clearing the environment in workers before env vars specified in this 491 | ; pool configuration are added. 492 | ; Setting to "no" will make all environment variables available to PHP code 493 | ; via getenv(), $_ENV and $_SERVER. 494 | ; Default Value: yes 495 | ;clear_env = no 496 | 497 | ; Limits the extensions of the main script FPM will allow to parse. This can 498 | ; prevent configuration mistakes on the web server side. You should only limit 499 | ; FPM to .php extensions to prevent malicious users to use other extensions to 500 | ; exectute php code. 501 | ; Note: set an empty value to allow all extensions. 502 | ; Default Value: .php 503 | ;security.limit_extensions = .php .php3 .php4 .php5 504 | 505 | ; Pass environment variables like LD_LIBRARY_PATH. All $VARIABLEs are taken from 506 | ; the current environment. 507 | ; Default Value: clean env 508 | env[HOSTNAME] = $HOSTNAME 509 | env[PATH] = /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin 510 | env[TMP] = /data/nextcloud/tmp 511 | env[TMPDIR] = /data/nextcloud/tmp 512 | env[TEMP] = /data/nextcloud/tmp 513 | env[NEXTCLOUD_CONFIG_DIR]={{ data_dir }}/nextcloud/config 514 | env[MAGICK_CODER_MODULE_PATH] = /snap/nextcloud/current/php/usr/lib/ImageMagickCoders 515 | 516 | ; Additional php.ini defines, specific to this pool of workers. These settings 517 | ; overwrite the values previously defined in the php.ini. The directives are the 518 | ; same as the PHP SAPI: 519 | ; php_value/php_flag - you can set classic ini defines which can 520 | ; be overwritten from PHP call 'ini_set'. 521 | ; php_admin_value/php_admin_flag - these directives won't be overwritten by 522 | ; PHP call 'ini_set' 523 | ; For php_*flag, valid values are on, off, 1, 0, true, false, yes or no. 524 | 525 | ; Defining 'extension' will load the corresponding shared extension from 526 | ; extension_dir. Defining 'disable_functions' or 'disable_classes' will not 527 | ; overwrite previously defined php.ini values, but will append the new value 528 | ; instead. 529 | 530 | ; Note: path INI options can be relative and will be expanded with the prefix 531 | ; (pool, global or /opt/syncloud-nextcloud/php) 532 | 533 | ; Default Value: nothing is defined by default except the values in php.ini and 534 | ; specified at startup with the -d argument 535 | ;php_admin_value[sendmail_path] = /usr/sbin/sendmail -t -i -f www@my.domain.com 536 | ;php_flag[display_errors] = off 537 | php_admin_value[error_log] = syslog 538 | php_admin_flag[log_errors] = on 539 | ;php_admin_value[memory_limit] = 32M 540 | -------------------------------------------------------------------------------- /config/postgresql.conf: -------------------------------------------------------------------------------- 1 | # ----------------------------- 2 | # PostgreSQL configuration file 3 | # ----------------------------- 4 | # 5 | # This file consists of lines of the form: 6 | # 7 | # name = value 8 | # 9 | # (The "=" is optional.) Whitespace may be used. Comments are introduced with 10 | # "#" anywhere on a line. The complete list of parameter names and allowed 11 | # values can be found in the PostgreSQL documentation. 12 | # 13 | # The commented-out settings shown in this file represent the default values. 14 | # Re-commenting a setting is NOT sufficient to revert it to the default value; 15 | # you need to reload the server. 16 | # 17 | # This file is read on server startup and when the server receives a SIGHUP 18 | # signal. If you edit the file on a running system, you have to SIGHUP the 19 | # server for the changes to take effect, or use "pg_ctl reload". Some 20 | # parameters, which are marked below, require a server shutdown and restart to 21 | # take effect. 22 | # 23 | # Any parameter can also be given as a command-line option to the server, e.g., 24 | # "postgres -c log_connections=on". Some parameters can be changed at run time 25 | # with the "SET" SQL command. 26 | # 27 | # Memory units: kB = kilobytes Time units: ms = milliseconds 28 | # MB = megabytes s = seconds 29 | # GB = gigabytes min = minutes 30 | # TB = terabytes h = hours 31 | # d = days 32 | 33 | 34 | #------------------------------------------------------------------------------ 35 | # FILE LOCATIONS 36 | #------------------------------------------------------------------------------ 37 | 38 | # The default values of these variables are driven from the -D command-line 39 | # option or PGDATA environment variable, represented here as ConfigDir. 40 | 41 | #data_directory = 'ConfigDir' # use data in another directory 42 | # (change requires restart) 43 | #hba_file = 'ConfigDir/pg_hba.conf' # host-based authentication file 44 | # (change requires restart) 45 | #ident_file = 'ConfigDir/pg_ident.conf' # ident configuration file 46 | # (change requires restart) 47 | 48 | # If external_pid_file is not explicitly set, no extra PID file is written. 49 | #external_pid_file = '' # write an extra PID file 50 | # (change requires restart) 51 | 52 | 53 | #------------------------------------------------------------------------------ 54 | # CONNECTIONS AND AUTHENTICATION 55 | #------------------------------------------------------------------------------ 56 | 57 | # - Connection Settings - 58 | 59 | listen_addresses = '' # what IP address(es) to listen on; 60 | # comma-separated list of addresses; 61 | # defaults to 'localhost'; use '*' for all 62 | # (change requires restart) 63 | port = {{ db_psql_port }} # (change requires restart) 64 | #max_connections = 100 # (change requires restart) 65 | # Note: Increasing max_connections costs ~400 bytes of shared memory per 66 | # connection slot, plus lock space (see max_locks_per_transaction). 67 | #superuser_reserved_connections = 3 # (change requires restart) 68 | unix_socket_directories = '{{ database_dir }}' # comma-separated list of directories 69 | # (change requires restart) 70 | #unix_socket_group = '' # (change requires restart) 71 | #unix_socket_permissions = 0777 # begin with 0 to use octal notation 72 | # (change requires restart) 73 | #bonjour = off # advertise server via Bonjour 74 | # (change requires restart) 75 | #bonjour_name = '' # defaults to the computer name 76 | # (change requires restart) 77 | 78 | # - Security and Authentication - 79 | 80 | #authentication_timeout = 1min # 1s-600s 81 | #ssl = off # (change requires restart) 82 | #ssl_ciphers = 'HIGH:MEDIUM:+3DES:!aNULL' # allowed SSL ciphers 83 | # (change requires restart) 84 | #ssl_prefer_server_ciphers = on # (change requires restart) 85 | #ssl_ecdh_curve = 'prime256v1' # (change requires restart) 86 | #ssl_renegotiation_limit = 512MB # amount of data between renegotiations 87 | #ssl_cert_file = 'server.crt' # (change requires restart) 88 | #ssl_key_file = 'server.key' # (change requires restart) 89 | #ssl_ca_file = '' # (change requires restart) 90 | #ssl_crl_file = '' # (change requires restart) 91 | #password_encryption = on 92 | #db_user_namespace = off 93 | 94 | # GSSAPI using Kerberos 95 | #krb_server_keyfile = '' 96 | #krb_caseins_users = off 97 | 98 | # - TCP Keepalives - 99 | # see "man 7 tcp" for details 100 | 101 | #tcp_keepalives_idle = 0 # TCP_KEEPIDLE, in seconds; 102 | # 0 selects the system default 103 | #tcp_keepalives_interval = 0 # TCP_KEEPINTVL, in seconds; 104 | # 0 selects the system default 105 | #tcp_keepalives_count = 0 # TCP_KEEPCNT; 106 | # 0 selects the system default 107 | 108 | 109 | #------------------------------------------------------------------------------ 110 | # RESOURCE USAGE (except WAL) 111 | #------------------------------------------------------------------------------ 112 | 113 | # - Memory - 114 | 115 | #shared_buffers = 32MB # min 128kB 116 | # (change requires restart) 117 | #huge_pages = try # on, off, or try 118 | # (change requires restart) 119 | #temp_buffers = 8MB # min 800kB 120 | #max_prepared_transactions = 0 # zero disables the feature 121 | # (change requires restart) 122 | # Note: Increasing max_prepared_transactions costs ~600 bytes of shared memory 123 | # per transaction slot, plus lock space (see max_locks_per_transaction). 124 | # It is not advisable to set max_prepared_transactions nonzero unless you 125 | # actively intend to use prepared transactions. 126 | #work_mem = 4MB # min 64kB 127 | #maintenance_work_mem = 64MB # min 1MB 128 | #autovacuum_work_mem = -1 # min 1MB, or -1 to use maintenance_work_mem 129 | #max_stack_depth = 2MB # min 100kB 130 | #dynamic_shared_memory_type = posix # the default is the first option 131 | # supported by the operating system: 132 | # posix 133 | # sysv 134 | # windows 135 | # mmap 136 | # use none to disable dynamic shared memory 137 | 138 | # - Disk - 139 | 140 | #temp_file_limit = -1 # limits per-session temp file space 141 | # in kB, or -1 for no limit 142 | 143 | # - Kernel Resource Usage - 144 | 145 | #max_files_per_process = 1000 # min 25 146 | # (change requires restart) 147 | #shared_preload_libraries = '' # (change requires restart) 148 | 149 | # - Cost-Based Vacuum Delay - 150 | 151 | #vacuum_cost_delay = 0 # 0-100 milliseconds 152 | #vacuum_cost_page_hit = 1 # 0-10000 credits 153 | #vacuum_cost_page_miss = 10 # 0-10000 credits 154 | #vacuum_cost_page_dirty = 20 # 0-10000 credits 155 | #vacuum_cost_limit = 200 # 1-10000 credits 156 | 157 | # - Background Writer - 158 | 159 | #bgwriter_delay = 200ms # 10-10000ms between rounds 160 | #bgwriter_lru_maxpages = 100 # 0-1000 max buffers written/round 161 | #bgwriter_lru_multiplier = 2.0 # 0-10.0 multipler on buffers scanned/round 162 | 163 | # - Asynchronous Behavior - 164 | 165 | #effective_io_concurrency = 1 # 1-1000; 0 disables prefetching 166 | #max_worker_processes = 8 167 | 168 | 169 | #------------------------------------------------------------------------------ 170 | # WRITE AHEAD LOG 171 | #------------------------------------------------------------------------------ 172 | 173 | # - Settings - 174 | 175 | #wal_level = minimal # minimal, archive, hot_standby, or logical 176 | # (change requires restart) 177 | #fsync = on # turns forced synchronization on or off 178 | #synchronous_commit = on # synchronization level; 179 | # off, local, remote_write, or on 180 | #wal_sync_method = fsync # the default is the first option 181 | # supported by the operating system: 182 | # open_datasync 183 | # fdatasync (default on Linux) 184 | # fsync 185 | # fsync_writethrough 186 | # open_sync 187 | #full_page_writes = on # recover from partial page writes 188 | #wal_log_hints = off # also do full page writes of non-critical updates 189 | # (change requires restart) 190 | #wal_buffers = -1 # min 32kB, -1 sets based on shared_buffers 191 | # (change requires restart) 192 | #wal_writer_delay = 200ms # 1-10000 milliseconds 193 | 194 | #commit_delay = 0 # range 0-100000, in microseconds 195 | #commit_siblings = 5 # range 1-1000 196 | 197 | # - Checkpoints - 198 | 199 | #checkpoint_segments = 3 # in logfile segments, min 1, 16MB each 200 | #checkpoint_timeout = 5min # range 30s-1h 201 | #checkpoint_completion_target = 0.5 # checkpoint target duration, 0.0 - 1.0 202 | #checkpoint_warning = 30s # 0 disables 203 | 204 | # - Archiving - 205 | 206 | #archive_mode = off # allows archiving to be done 207 | # (change requires restart) 208 | #archive_command = '' # command to use to archive a logfile segment 209 | # placeholders: %p = path of file to archive 210 | # %f = file name only 211 | # e.g. 'test ! -f /mnt/server/archivedir/%f && cp %p /mnt/server/archivedir/%f' 212 | #archive_timeout = 0 # force a logfile segment switch after this 213 | # number of seconds; 0 disables 214 | 215 | 216 | #------------------------------------------------------------------------------ 217 | # REPLICATION 218 | #------------------------------------------------------------------------------ 219 | 220 | # - Sending Server(s) - 221 | 222 | # Set these on the master and on any standby that will send replication data. 223 | 224 | #max_wal_senders = 0 # max number of walsender processes 225 | # (change requires restart) 226 | #wal_keep_segments = 0 # in logfile segments, 16MB each; 0 disables 227 | #wal_sender_timeout = 60s # in milliseconds; 0 disables 228 | 229 | #max_replication_slots = 0 # max number of replication slots 230 | # (change requires restart) 231 | 232 | # - Master Server - 233 | 234 | # These settings are ignored on a standby server. 235 | 236 | #synchronous_standby_names = '' # standby servers that provide sync rep 237 | # comma-separated list of application_name 238 | # from standby(s); '*' = all 239 | #vacuum_defer_cleanup_age = 0 # number of xacts by which cleanup is delayed 240 | 241 | # - Standby Servers - 242 | 243 | # These settings are ignored on a master server. 244 | 245 | #hot_standby = off # "on" allows queries during recovery 246 | # (change requires restart) 247 | #max_standby_archive_delay = 30s # max delay before canceling queries 248 | # when reading WAL from archive; 249 | # -1 allows indefinite delay 250 | #max_standby_streaming_delay = 30s # max delay before canceling queries 251 | # when reading streaming WAL; 252 | # -1 allows indefinite delay 253 | #wal_receiver_status_interval = 10s # send replies at least this often 254 | # 0 disables 255 | #hot_standby_feedback = off # send info from standby to prevent 256 | # query conflicts 257 | #wal_receiver_timeout = 60s # time that receiver waits for 258 | # communication from master 259 | # in milliseconds; 0 disables 260 | 261 | 262 | #------------------------------------------------------------------------------ 263 | # QUERY TUNING 264 | #------------------------------------------------------------------------------ 265 | 266 | # - Planner Method Configuration - 267 | 268 | #enable_bitmapscan = on 269 | #enable_hashagg = on 270 | #enable_hashjoin = on 271 | #enable_indexscan = on 272 | #enable_indexonlyscan = on 273 | #enable_material = on 274 | #enable_mergejoin = on 275 | #enable_nestloop = on 276 | #enable_seqscan = on 277 | #enable_sort = on 278 | #enable_tidscan = on 279 | 280 | # - Planner Cost Constants - 281 | 282 | #seq_page_cost = 1.0 # measured on an arbitrary scale 283 | #random_page_cost = 4.0 # same scale as above 284 | #cpu_tuple_cost = 0.01 # same scale as above 285 | #cpu_index_tuple_cost = 0.005 # same scale as above 286 | #cpu_operator_cost = 0.0025 # same scale as above 287 | #effective_cache_size = 4GB 288 | 289 | # - Genetic Query Optimizer - 290 | 291 | #geqo = on 292 | #geqo_threshold = 12 293 | #geqo_effort = 5 # range 1-10 294 | #geqo_pool_size = 0 # selects default based on effort 295 | #geqo_generations = 0 # selects default based on effort 296 | #geqo_selection_bias = 2.0 # range 1.5-2.0 297 | #geqo_seed = 0.0 # range 0.0-1.0 298 | 299 | # - Other Planner Options - 300 | 301 | #default_statistics_target = 100 # range 1-10000 302 | #constraint_exclusion = partition # on, off, or partition 303 | #cursor_tuple_fraction = 0.1 # range 0.0-1.0 304 | #from_collapse_limit = 8 305 | #join_collapse_limit = 8 # 1 disables collapsing of explicit 306 | # JOIN clauses 307 | 308 | 309 | #------------------------------------------------------------------------------ 310 | # ERROR REPORTING AND LOGGING 311 | #------------------------------------------------------------------------------ 312 | 313 | # - Where to Log - 314 | 315 | log_destination = 'syslog' # Valid values are combinations of 316 | # stderr, csvlog, syslog, and eventlog, 317 | # depending on platform. csvlog 318 | # requires logging_collector to be on. 319 | 320 | # This is used when logging to stderr: 321 | #logging_collector = off # Enable capturing of stderr and csvlog 322 | # into log files. Required to be on for 323 | # csvlogs. 324 | # (change requires restart) 325 | 326 | # These are only used if logging_collector is on: 327 | #log_directory = 'pg_log' # directory where log files are written, 328 | # can be absolute or relative to PGDATA 329 | #log_filename = 'postgresql-%Y-%m-%d_%H%M%S.log' # log file name pattern, 330 | # can include strftime() escapes 331 | #log_file_mode = 0600 # creation mode for log files, 332 | # begin with 0 to use octal notation 333 | #log_truncate_on_rotation = off # If on, an existing log file with the 334 | # same name as the new log file will be 335 | # truncated rather than appended to. 336 | # But such truncation only occurs on 337 | # time-driven rotation, not on restarts 338 | # or size-driven rotation. Default is 339 | # off, meaning append to existing files 340 | # in all cases. 341 | #log_rotation_age = 1d # Automatic rotation of logfiles will 342 | # happen after that time. 0 disables. 343 | #log_rotation_size = 10MB # Automatic rotation of logfiles will 344 | # happen after that much log output. 345 | # 0 disables. 346 | 347 | # These are relevant when logging to syslog: 348 | #syslog_facility = 'LOCAL0' 349 | syslog_ident = 'nextcloud.postgres' 350 | 351 | # This is only relevant when logging to eventlog (win32): 352 | #event_source = 'PostgreSQL' 353 | 354 | # - When to Log - 355 | 356 | #client_min_messages = notice # values in order of decreasing detail: 357 | # debug5 358 | # debug4 359 | # debug3 360 | # debug2 361 | # debug1 362 | # log 363 | # notice 364 | # warning 365 | # error 366 | 367 | #log_min_messages = warning # values in order of decreasing detail: 368 | # debug5 369 | # debug4 370 | # debug3 371 | # debug2 372 | # debug1 373 | # info 374 | # notice 375 | # warning 376 | # error 377 | # log 378 | # fatal 379 | # panic 380 | 381 | #log_min_error_statement = error # values in order of decreasing detail: 382 | # debug5 383 | # debug4 384 | # debug3 385 | # debug2 386 | # debug1 387 | # info 388 | # notice 389 | # warning 390 | # error 391 | # log 392 | # fatal 393 | # panic (effectively off) 394 | 395 | #log_min_duration_statement = -1 # -1 is disabled, 0 logs all statements 396 | # and their durations, > 0 logs only 397 | # statements running at least this number 398 | # of milliseconds 399 | 400 | 401 | # - What to Log - 402 | 403 | #debug_print_parse = off 404 | #debug_print_rewritten = off 405 | #debug_print_plan = off 406 | #debug_pretty_print = on 407 | #log_checkpoints = off 408 | #log_connections = off 409 | #log_disconnections = off 410 | #log_duration = off 411 | #log_error_verbosity = default # terse, default, or verbose messages 412 | #log_hostname = off 413 | #log_line_prefix = '' # special values: 414 | # %a = application name 415 | # %u = user name 416 | # %d = database name 417 | # %r = remote host and port 418 | # %h = remote host 419 | # %p = process ID 420 | # %t = timestamp without milliseconds 421 | # %m = timestamp with milliseconds 422 | # %i = command tag 423 | # %e = SQL state 424 | # %c = session ID 425 | # %l = session line number 426 | # %s = session start timestamp 427 | # %v = virtual transaction ID 428 | # %x = transaction ID (0 if none) 429 | # %q = stop here in non-session 430 | # processes 431 | # %% = '%' 432 | # e.g. '<%u%%%d> ' 433 | #log_lock_waits = off # log lock waits >= deadlock_timeout 434 | #log_statement = 'none' # none, ddl, mod, all 435 | #log_temp_files = -1 # log temporary files equal or larger 436 | # than the specified size in kilobytes; 437 | # -1 disables, 0 logs all temp files 438 | #log_timezone = 'GMT' 439 | 440 | 441 | #------------------------------------------------------------------------------ 442 | # RUNTIME STATISTICS 443 | #------------------------------------------------------------------------------ 444 | 445 | # - Query/Index Statistics Collector - 446 | 447 | #track_activities = on 448 | #track_counts = on 449 | #track_io_timing = off 450 | #track_functions = none # none, pl, all 451 | #track_activity_query_size = 1024 # (change requires restart) 452 | #update_process_title = on 453 | #stats_temp_directory = 'pg_stat_tmp' 454 | 455 | 456 | # - Statistics Monitoring - 457 | 458 | #log_parser_stats = off 459 | #log_planner_stats = off 460 | #log_executor_stats = off 461 | #log_statement_stats = off 462 | 463 | 464 | #------------------------------------------------------------------------------ 465 | # AUTOVACUUM PARAMETERS 466 | #------------------------------------------------------------------------------ 467 | 468 | #autovacuum = on # Enable autovacuum subprocess? 'on' 469 | # requires track_counts to also be on. 470 | #log_autovacuum_min_duration = -1 # -1 disables, 0 logs all actions and 471 | # their durations, > 0 logs only 472 | # actions running at least this number 473 | # of milliseconds. 474 | #autovacuum_max_workers = 3 # max number of autovacuum subprocesses 475 | # (change requires restart) 476 | #autovacuum_naptime = 1min # time between autovacuum runs 477 | #autovacuum_vacuum_threshold = 50 # min number of row updates before 478 | # vacuum 479 | #autovacuum_analyze_threshold = 50 # min number of row updates before 480 | # analyze 481 | #autovacuum_vacuum_scale_factor = 0.2 # fraction of table size before vacuum 482 | #autovacuum_analyze_scale_factor = 0.1 # fraction of table size before analyze 483 | #autovacuum_freeze_max_age = 200000000 # maximum XID age before forced vacuum 484 | # (change requires restart) 485 | #autovacuum_multixact_freeze_max_age = 400000000 # maximum multixact age 486 | # before forced vacuum 487 | # (change requires restart) 488 | #autovacuum_vacuum_cost_delay = 20ms # default vacuum cost delay for 489 | # autovacuum, in milliseconds; 490 | # -1 means use vacuum_cost_delay 491 | #autovacuum_vacuum_cost_limit = -1 # default vacuum cost limit for 492 | # autovacuum, -1 means use 493 | # vacuum_cost_limit 494 | 495 | 496 | #------------------------------------------------------------------------------ 497 | # CLIENT CONNECTION DEFAULTS 498 | #------------------------------------------------------------------------------ 499 | 500 | # - Statement Behavior - 501 | 502 | #search_path = '"$user",public' # schema names 503 | #default_tablespace = '' # a tablespace name, '' uses the default 504 | #temp_tablespaces = '' # a list of tablespace names, '' uses 505 | # only default tablespace 506 | #check_function_bodies = on 507 | #default_transaction_isolation = 'read committed' 508 | #default_transaction_read_only = off 509 | #default_transaction_deferrable = off 510 | #session_replication_role = 'origin' 511 | #statement_timeout = 0 # in milliseconds, 0 is disabled 512 | #lock_timeout = 0 # in milliseconds, 0 is disabled 513 | #vacuum_freeze_min_age = 50000000 514 | #vacuum_freeze_table_age = 150000000 515 | #vacuum_multixact_freeze_min_age = 5000000 516 | #vacuum_multixact_freeze_table_age = 150000000 517 | #bytea_output = 'hex' # hex, escape 518 | #xmlbinary = 'base64' 519 | #xmloption = 'content' 520 | 521 | # - Locale and Formatting - 522 | 523 | #datestyle = 'iso, mdy' 524 | #intervalstyle = 'postgres' 525 | #timezone = 'GMT' 526 | #timezone_abbreviations = 'Default' # Select the set of available time zone 527 | # abbreviations. Currently, there are 528 | # Default 529 | # Australia (historical usage) 530 | # India 531 | # You can create your own file in 532 | # share/timezonesets/. 533 | #extra_float_digits = 0 # min -15, max 3 534 | #client_encoding = sql_ascii # actually, defaults to database 535 | # encoding 536 | 537 | # These settings are initialized by initdb, but they can be changed. 538 | #lc_messages = 'C' # locale for system error message 539 | # strings 540 | #lc_monetary = 'C' # locale for monetary formatting 541 | #lc_numeric = 'C' # locale for number formatting 542 | #lc_time = 'C' # locale for time formatting 543 | 544 | # default configuration for text search 545 | #default_text_search_config = 'pg_catalog.simple' 546 | 547 | # - Other Defaults - 548 | 549 | #dynamic_library_path = '$libdir' 550 | #local_preload_libraries = '' 551 | #session_preload_libraries = '' 552 | 553 | 554 | #------------------------------------------------------------------------------ 555 | # LOCK MANAGEMENT 556 | #------------------------------------------------------------------------------ 557 | 558 | #deadlock_timeout = 1s 559 | #max_locks_per_transaction = 64 # min 10 560 | # (change requires restart) 561 | # Note: Each lock table slot uses ~270 bytes of shared memory, and there are 562 | # max_locks_per_transaction * (max_connections + max_prepared_transactions) 563 | # lock table slots. 564 | #max_pred_locks_per_transaction = 64 # min 10 565 | # (change requires restart) 566 | 567 | 568 | #------------------------------------------------------------------------------ 569 | # VERSION/PLATFORM COMPATIBILITY 570 | #------------------------------------------------------------------------------ 571 | 572 | # - Previous PostgreSQL Versions - 573 | 574 | #array_nulls = on 575 | #backslash_quote = safe_encoding # on, off, or safe_encoding 576 | #default_with_oids = off 577 | #escape_string_warning = on 578 | #lo_compat_privileges = off 579 | #quote_all_identifiers = off 580 | #sql_inheritance = on 581 | #standard_conforming_strings = on 582 | #synchronize_seqscans = on 583 | 584 | # - Other Platforms and Clients - 585 | 586 | #transform_null_equals = off 587 | 588 | 589 | #------------------------------------------------------------------------------ 590 | # ERROR HANDLING 591 | #------------------------------------------------------------------------------ 592 | 593 | #exit_on_error = off # terminate session on any error? 594 | #restart_after_crash = on # reinitialize after backend crash? 595 | 596 | 597 | #------------------------------------------------------------------------------ 598 | # CONFIG FILE INCLUDES 599 | #------------------------------------------------------------------------------ 600 | 601 | # These options allow settings to be loaded from files other than the 602 | # default postgresql.conf. 603 | 604 | #include_dir = 'conf.d' # include files ending in '.conf' from 605 | # directory 'conf.d' 606 | #include_if_exists = 'exists.conf' # include file only if it exists 607 | #include = 'special.conf' # include file 608 | 609 | 610 | #------------------------------------------------------------------------------ 611 | # CUSTOMIZED OPTIONS 612 | #------------------------------------------------------------------------------ 613 | 614 | # Add settings for extensions here 615 | -------------------------------------------------------------------------------- /config/redis.conf: -------------------------------------------------------------------------------- 1 | port 0 2 | bind 127.0.0.1 3 | unixsocket /var/snap/nextcloud/current/redis.sock 4 | unixsocketperm 770 5 | maxmemory 50M -------------------------------------------------------------------------------- /download.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh -ex 2 | 3 | DIR=$( cd "$( dirname "$0" )" && pwd ) 4 | cd ${DIR} 5 | 6 | ARCH=$(uname -m) 7 | DOWNLOAD_URL=https://github.com/syncloud/3rdparty/releases/download/ 8 | VERSION=$1 9 | apt update 10 | apt install -y wget bzip2 11 | 12 | BUILD_DIR=${DIR}/build/snap 13 | mkdir -p $BUILD_DIR 14 | 15 | cd ${DIR}/build 16 | wget https://download.nextcloud.com/server/releases/nextcloud-${VERSION}.tar.bz2 -O nextcloud.tar.bz2 17 | tar xf nextcloud.tar.bz2 18 | mv nextcloud ${BUILD_DIR} 19 | -------------------------------------------------------------------------------- /hooks/access-change: -------------------------------------------------------------------------------- 1 | #!/snap/nextcloud/current/python/bin/python 2 | 3 | from installer import Installer 4 | Installer().on_domain_change() 5 | -------------------------------------------------------------------------------- /hooks/access-change.py: -------------------------------------------------------------------------------- 1 | import subprocess 2 | 3 | print(subprocess.check_output('snap run nextcloud.access-change', shell=True)) 4 | 5 | -------------------------------------------------------------------------------- /hooks/backup-pre-stop: -------------------------------------------------------------------------------- 1 | #!/snap/nextcloud/current/python/bin/python 2 | 3 | from installer import Installer 4 | Installer().backup_pre_stop() 5 | -------------------------------------------------------------------------------- /hooks/installer.py: -------------------------------------------------------------------------------- 1 | from os.path import isfile 2 | from os.path import join 3 | from os.path import realpath 4 | 5 | import logging 6 | import re 7 | import shutil 8 | import uuid 9 | from crontab import CronTab 10 | from subprocess import check_output 11 | from syncloudlib import fs, linux, gen, logger 12 | from syncloudlib.application import paths, urls, storage, service 13 | 14 | from octools import OCConsole, OCConfig 15 | from postgres import Database 16 | 17 | APP_NAME = 'nextcloud' 18 | 19 | USER_NAME = APP_NAME 20 | DB_NAME = APP_NAME 21 | DB_USER = APP_NAME 22 | DB_PASSWORD = APP_NAME 23 | OCC_RUNNER_PATH = 'bin/occ-runner' 24 | LOG_PATH = 'log/{0}.log'.format(APP_NAME) 25 | CRON_USER = APP_NAME 26 | APP_CONFIG_PATH = '{0}/config'.format(APP_NAME) 27 | PSQL_PORT = 5436 28 | 29 | SYSTEMD_NGINX = '{0}.nginx'.format(APP_NAME) 30 | SYSTEMD_PHP_FPM = '{0}.php-fpm'.format(APP_NAME) 31 | SYSTEMD_POSTGRESQL = '{0}.postgresql'.format(APP_NAME) 32 | 33 | class Installer: 34 | def __init__(self): 35 | if not logger.factory_instance: 36 | logger.init(logging.DEBUG, True) 37 | 38 | self.log = logger.get_logger('nextcloud_installer') 39 | self.app_dir = paths.get_app_dir(APP_NAME) 40 | self.common_dir = paths.get_data_dir(APP_NAME) 41 | self.data_dir = join('/var/snap', APP_NAME, 'current') 42 | self.config_dir = join(self.data_dir, 'config') 43 | self.extra_apps_dir = join(self.data_dir, 'extra-apps') 44 | self.occ = OCConsole(join(self.app_dir, OCC_RUNNER_PATH)) 45 | self.nextcloud_config_path = join(self.data_dir, 'nextcloud', 'config') 46 | self.nextcloud_config_file = join(self.nextcloud_config_path, 'config.php') 47 | self.cron = Cron(CRON_USER) 48 | self.db = Database(self.app_dir, self.data_dir, self.config_dir, PSQL_PORT) 49 | self.oc_config = OCConfig(join(self.app_dir, 'bin/nextcloud-config')) 50 | 51 | def install_config(self): 52 | 53 | home_folder = join('/home', USER_NAME) 54 | linux.useradd(USER_NAME, home_folder=home_folder) 55 | storage.init_storage(APP_NAME, USER_NAME) 56 | templates_path = join(self.app_dir, 'config') 57 | 58 | variables = { 59 | 'app_dir': self.app_dir, 60 | 'common_dir': self.common_dir, 61 | 'data_dir': self.data_dir, 62 | 'db_psql_port': PSQL_PORT, 63 | 'database_dir': self.db.database_dir, 64 | 'config_dir': self.config_dir, 65 | 'domain': urls.get_app_domain_name(APP_NAME) 66 | } 67 | gen.generate_files(templates_path, self.config_dir, variables) 68 | 69 | fs.makepath(self.nextcloud_config_path) 70 | fs.makepath(join(self.common_dir, 'log')) 71 | fs.makepath(join(self.common_dir, 'nginx')) 72 | 73 | fs.makepath(self.extra_apps_dir) 74 | 75 | self.fix_permissions() 76 | 77 | def install(self): 78 | self.install_config() 79 | 80 | default_config_file = join(self.config_dir, 'config.php') 81 | shutil.copy(default_config_file, self.nextcloud_config_file) 82 | self.fix_config_permission() 83 | 84 | self.db.init() 85 | self.db.init_config() 86 | 87 | def pre_refresh(self): 88 | self.db.backup() 89 | 90 | def post_refresh(self): 91 | self.install_config() 92 | self.migrate_nextcloud_config_file() 93 | self.fix_version_specific_dbhost() 94 | 95 | self.db.remove() 96 | self.db.init() 97 | 98 | self.db.init_config() 99 | 100 | def configure(self): 101 | 102 | if self.installed(): 103 | self.upgrade() 104 | else: 105 | self.initialize() 106 | 107 | app_storage_dir = storage.get_storage_dir(APP_NAME) 108 | self.occ.run('ldap:set-config s01 ldapEmailAttribute mail') 109 | self.occ.run('config:system:set apps_paths 1 path --value="{0}"'.format(self.extra_apps_dir)) 110 | self.occ.run('config:system:set dbhost --value="{0}"'.format(self.db.database_host)) 111 | # migrate to systemd cron units 112 | self.cron.remove() 113 | self.cron.create() 114 | 115 | self.occ.run("config:system:set memcache.local --value='\\OC\\Memcache\\APCu'") 116 | self.occ.run("config:system:set redis host --value=/var/snap/nextcloud/current/redis.sock") 117 | self.occ.run("config:system:set redis port --value=0") 118 | self.occ.run("config:system:set memcache.distributed --value='\\OC\\Memcache\\Redis'") 119 | self.occ.run("config:system:set memcache.locking --value='\\OC\\Memcache\\Redis'") 120 | self.occ.run("config:system:set maintenance_window_start --value=1") 121 | self.oc_config.set_value('loglevel', '2') 122 | self.oc_config.set_value('logfile', join(self.common_dir, LOG_PATH)) 123 | real_app_storage_dir = realpath(app_storage_dir) 124 | self.oc_config.set_value('datadirectory', real_app_storage_dir) 125 | # oc_config.set_value('integrity.check.disabled', 'true') 126 | self.oc_config.set_value('mail_smtpmode', 'smtp') 127 | self.oc_config.set_value('mail_smtphost', 'localhost:25') 128 | # oc_config.set_value('mail_smtpsecure', '') 129 | self.oc_config.set_value('mail_smtpauth', 'false') 130 | # oc_config.set_value('mail_smtpname', '') 131 | # oc_config.set_value('mail_smtppassword', '') 132 | 133 | self.occ.run("app:disable logreader") 134 | 135 | self.on_domain_change() 136 | 137 | self.fix_permissions() 138 | 139 | def fix_permissions(self): 140 | check_output('chown -R {0}.{0} {1}'.format(USER_NAME, self.common_dir), shell=True) 141 | check_output('chown -R {0}.{0} {1}/'.format(USER_NAME, self.data_dir), shell=True) 142 | 143 | def migrate_nextcloud_config_file(self): 144 | if not isfile(self.nextcloud_config_file): 145 | old_nextcloud_config_file = join(self.common_dir, 'nextcloud', 'config', 'config.php') 146 | if isfile(old_nextcloud_config_file): 147 | shutil.copy(old_nextcloud_config_file, self.nextcloud_config_file) 148 | self.fix_config_permission() 149 | 150 | def fix_config_permission(self): 151 | fs.chownpath(self.nextcloud_config_file, USER_NAME) 152 | 153 | def fix_version_specific_dbhost(self): 154 | content = self.read_nextcloud_config() 155 | pattern = r"'dbhost'\s*=>\s*'.*?'" 156 | replacement = "'dbhost' => '{0}'".format(self.db.database_host) 157 | new_content = re.sub(pattern, replacement, content) 158 | self.write_nextcloud_config(new_content) 159 | self.fix_config_permission() 160 | 161 | def read_nextcloud_config(self): 162 | with open(self.nextcloud_config_file) as f: 163 | return f.read() 164 | 165 | def write_nextcloud_config(self, content): 166 | with open(self.nextcloud_config_file, "w") as f: 167 | f.write(content) 168 | 169 | def installed(self): 170 | return 'installed' in open(self.nextcloud_config_file).read().strip() 171 | 172 | def upgrade(self): 173 | self.db.restore() 174 | self.prepare_storage() 175 | status = self.occ.run('status') 176 | self.log.info('status: {0}'.format(status)) 177 | # if 'require upgrade' in status: 178 | self.log.info('upgrading nextcloud') 179 | # self.occ.run('maintenance:mode --on') 180 | self.occ.run('upgrade') 181 | # self.occ.run('app:update --all') 182 | self.occ.run('maintenance:mode --off') 183 | self.occ.run('db:add-missing-indices') 184 | self.occ.run('db:add-missing-columns') 185 | self.occ.run('db:add-missing-primary-keys') 186 | # else: 187 | # self.log.info('not upgrading nextcloud') 188 | 189 | def initialize(self): 190 | self.prepare_storage() 191 | app_storage_dir = storage.get_storage_dir(APP_NAME) 192 | self.db.execute('postgres', DB_USER, "ALTER USER {0} WITH PASSWORD '{1}';".format(DB_USER, DB_PASSWORD)) 193 | self.db.execute('postgres', DB_USER, "CREATE DATABASE nextcloud OWNER {0} TEMPLATE template0 ENCODING 'UTF8';".format(DB_USER)) 194 | self.db.execute('postgres', DB_USER, "GRANT CREATE ON SCHEMA public TO {0};".format(DB_USER)) 195 | 196 | real_app_storage_dir = realpath(app_storage_dir) 197 | install_user_name = 'installer-{0}'.format(uuid.uuid4().hex) 198 | install_user_password = uuid.uuid4().hex 199 | self.occ.run('maintenance:install --database pgsql --database-host {0}:{1}' 200 | ' --database-name nextcloud --database-user {2} --database-pass {3}' 201 | ' --admin-user {4} --admin-pass {5} --data-dir {6}' 202 | .format(self.db.get_database_path(), PSQL_PORT, DB_USER, DB_PASSWORD, 203 | install_user_name, install_user_password, real_app_storage_dir)) 204 | 205 | self.occ.run('app:enable user_ldap') 206 | 207 | # https://doc.owncloud.org/server/8.0/admin_manual/configuration_server/occ_command.html 208 | self.occ.run('ldap:create-empty-config') 209 | 210 | self.occ.run('ldap:set-config s01 ldapHost ldap://localhost') 211 | self.occ.run('ldap:set-config s01 ldapPort 389') 212 | self.occ.run('ldap:set-config s01 ldapAgentName cn=admin,dc=syncloud,dc=org') 213 | self.occ.run('ldap:set-config s01 ldapBase dc=syncloud,dc=org') 214 | self.occ.run('ldap:set-config s01 ldapAgentPassword syncloud') 215 | 216 | self.occ.run('ldap:set-config s01 hasMemberOfFilterSupport 0') 217 | self.occ.run('ldap:set-config s01 ldapLoginFilter "(&(|(objectclass=inetOrgPerson))(cn=%uid))"') 218 | 219 | self.occ.run('ldap:set-config s01 ldapUserFilter "(|(objectclass=inetOrgPerson))"') 220 | self.occ.run('ldap:set-config s01 ldapUserFilterObjectclass inetOrgPerson') 221 | 222 | self.occ.run('ldap:set-config s01 ldapBaseUsers ou=users,dc=syncloud,dc=org') 223 | self.occ.run('ldap:set-config s01 ldapUserDisplayName cn') 224 | self.occ.run('ldap:set-config s01 ldapExpertUsernameAttr cn') 225 | 226 | self.occ.run('ldap:set-config s01 ldapGroupFilterObjectclass posixGroup') 227 | self.occ.run('ldap:set-config s01 ldapGroupDisplayName cn') 228 | self.occ.run('ldap:set-config s01 ldapBaseGroups ou=groups,dc=syncloud,dc=org') 229 | self.occ.run('ldap:set-config s01 ldapGroupFilter "(&(|(objectclass=posixGroup)))"') 230 | # self.occ.run('ldap:set-config s01 ldapGroupFilterGroups syncloud') 231 | self.occ.run('ldap:set-config s01 ldapGroupMemberAssocAttr memberUid') 232 | 233 | self.occ.run('ldap:set-config s01 ldapTLS 0') 234 | self.occ.run('ldap:set-config s01 turnOffCertCheck 1') 235 | self.occ.run('ldap:set-config s01 ldapConfigurationActive 1') 236 | 237 | self.occ.run('db:convert-filecache-bigint') 238 | 239 | # cron takes a lot of time and fails the installation on big existing file storage 240 | self.cron.run() 241 | 242 | self.db.execute(DB_NAME, DB_USER, "select * from oc_ldap_group_mapping;") 243 | self.db.execute(DB_NAME, DB_USER, 244 | "update oc_ldap_group_mapping set owncloud_name = 'admin' where owncloud_name = 'syncloud';") 245 | 246 | self.occ.run('user:delete {0}'.format(install_user_name)) 247 | self.occ.run('db:add-missing-indices') 248 | self.occ.run('ldap:promote-group admin -y') 249 | 250 | def on_disk_change(self): 251 | storage.init_storage(APP_NAME, USER_NAME) 252 | self.prepare_storage() 253 | self.occ.run('config:system:delete instanceid') 254 | service.restart(SYSTEMD_PHP_FPM) 255 | service.restart(SYSTEMD_NGINX) 256 | 257 | def prepare_storage(self): 258 | app_storage_dir = storage.get_storage_dir(APP_NAME) 259 | ncdata = join(app_storage_dir, '.ncdata') 260 | fs.touchfile(ncdata) 261 | check_output('chown {0}. {1}'.format(USER_NAME, ncdata), shell=True) 262 | check_output('chmod 777 {0}'.format(app_storage_dir), shell=True) 263 | tmp_storage_path = join(app_storage_dir, 'tmp') 264 | fs.makepath(tmp_storage_path) 265 | fs.chownpath(tmp_storage_path, USER_NAME) 266 | real_app_storage_dir = realpath(app_storage_dir) 267 | self.fix_datadirectory(real_app_storage_dir) 268 | 269 | def on_domain_change(self): 270 | app_domain = urls.get_app_domain_name(APP_NAME) 271 | local_ip = check_output(["hostname", "-I"]).decode().split(" ")[0] 272 | self.oc_config.set_value('trusted_domains', "localhost {0} {1}".format(local_ip, app_domain)) 273 | self.oc_config.set_value('trusted_proxies', "127.0.0.1 {0}".format(local_ip)) 274 | self.oc_config.set_value('overwrite.cli.url', "https://{0}".format(app_domain)) 275 | 276 | def backup_pre_stop(self): 277 | self.pre_refresh() 278 | 279 | def restore_pre_start(self): 280 | self.post_refresh() 281 | 282 | def restore_post_start(self): 283 | self.configure() 284 | 285 | def fix_datadirectory(self, dir): 286 | content = self.read_nextcloud_config() 287 | pattern = r"'datadirectory'\s*=>\s*'.*?'" 288 | replacement = "'datadirectory' => '{0}'".format(dir) 289 | new_content = re.sub(pattern, replacement, content) 290 | self.write_nextcloud_config(new_content) 291 | self.fix_config_permission() 292 | 293 | 294 | class Cron: 295 | 296 | def __init__(self, cron_user): 297 | self.cron_cmd = '/usr/bin/snap run nextcloud.cron' 298 | self.cron_user = cron_user 299 | self.log = logger.get_logger('cron') 300 | 301 | def remove(self): 302 | print("remove crontab task") 303 | cron = CronTab(user=self.cron_user) 304 | for job in cron.find_command(self.cron_user): 305 | cron.remove(job) 306 | cron.write() 307 | 308 | def create(self): 309 | cron = CronTab(user=self.cron_user) 310 | print("create crontab task") 311 | ci_job = cron.new(command=self.cron_cmd) 312 | ci_job.setall('*/15 * * * *') 313 | cron.write() 314 | 315 | def run(self): 316 | self.log.info("running: {0}".format(self.cron_cmd)) 317 | self.log.info(check_output(self.cron_cmd, shell=True)) 318 | -------------------------------------------------------------------------------- /hooks/octools.py: -------------------------------------------------------------------------------- 1 | from subprocess import check_output, CalledProcessError 2 | from syncloudlib import logger 3 | 4 | 5 | class OCConsole: 6 | def __init__(self, occ_runner_path): 7 | self.occ_runner_path = occ_runner_path 8 | self.log = logger.get_logger('nextcloud_occ') 9 | 10 | def run(self, args): 11 | self.log.info(f'running: {self.occ_runner_path} {args}') 12 | try: 13 | output = check_output('{0} {1}'.format(self.occ_runner_path, args), shell=True).decode().strip() 14 | if output: 15 | self.log.info(output) 16 | return output 17 | except CalledProcessError as e: 18 | self.log.error("occ error: " + e.output.decode()) 19 | raise e 20 | 21 | 22 | class OCConfig: 23 | def __init__(self, oc_config_path): 24 | self.oc_config_path = oc_config_path 25 | self.log = logger.get_logger('nextcloud_config') 26 | 27 | def set_value(self, key, value): 28 | self.log.info('setting value: {0} = {1}'.format(key, value)) 29 | try: 30 | output = check_output('{0} {1} {2}'.format( 31 | self.oc_config_path, 32 | key, 33 | value), shell=True).decode().strip() 34 | if output: 35 | self.log.info(output) 36 | except CalledProcessError as e: 37 | self.log.error("occ config error: " + e.output.decode()) 38 | raise e 39 | -------------------------------------------------------------------------------- /hooks/postgres.py: -------------------------------------------------------------------------------- 1 | import shutil 2 | from os.path import join, isfile, isdir 3 | from subprocess import check_output, CalledProcessError 4 | 5 | from syncloudlib import logger 6 | 7 | 8 | class Database: 9 | 10 | def __init__(self, app_dir, data_dir, config_path, port): 11 | self.log = logger.get_logger('database') 12 | self.app_dir = app_dir 13 | self.config_path = config_path 14 | self.data_dir = data_dir 15 | self.postgresql_config = join(self.config_path, 'postgresql.conf') 16 | self.database_dir = join(self.data_dir, 'database') 17 | self.old_major_version_file = join(self.data_dir, 'db.major.version') 18 | self.new_major_version_file = join(self.app_dir, 'db.major.version') 19 | self.backup_file = join(self.data_dir, 'database.dump') 20 | self.database_host = '{0}:{1}'.format(self.database_dir, port) 21 | 22 | def get_database_path(self): 23 | return self.database_dir 24 | 25 | def remove(self): 26 | if not isfile(self.backup_file): 27 | raise Exception("Backup file does not exist: {0}".format(self.backup_file)) 28 | 29 | if isdir(self.database_dir): 30 | shutil.rmtree(self.database_dir) 31 | 32 | def init(self): 33 | self.run('{0}/bin/initdb.sh {1}'.format(self.app_dir, self.database_dir)) 34 | 35 | def init_config(self): 36 | shutil.copy(self.postgresql_config, self.database_dir) 37 | 38 | def execute(self, database, user, sql): 39 | self.run('snap run nextcloud.psql -U {0} -d {1} -c "{2}"'.format(user, database, sql)) 40 | 41 | def restore(self): 42 | self.run('snap run nextcloud.psql -f {0} postgres'.format(self.backup_file)) 43 | 44 | def backup(self): 45 | self.run('snap run nextcloud.pgdumpall -f {0}'.format(self.backup_file)) 46 | shutil.copy(self.new_major_version_file, self.old_major_version_file) 47 | 48 | def run(self, cmd): 49 | try: 50 | self.log.info("postgres executing: {0}".format(cmd)) 51 | output = check_output(cmd, shell=True).decode() 52 | self.log.info(output) 53 | except CalledProcessError as e: 54 | self.log.error("postgres error: " + e.output.decode()) 55 | raise e 56 | -------------------------------------------------------------------------------- /hooks/restore-post-start: -------------------------------------------------------------------------------- 1 | #!/snap/nextcloud/current/python/bin/python 2 | 3 | from installer import Installer 4 | Installer().restore_post_start() 5 | -------------------------------------------------------------------------------- /hooks/restore-pre-start: -------------------------------------------------------------------------------- 1 | #!/snap/nextcloud/current/python/bin/python 2 | 3 | from installer import Installer 4 | Installer().restore_pre_start() 5 | -------------------------------------------------------------------------------- /hooks/storage-change: -------------------------------------------------------------------------------- 1 | #!/snap/nextcloud/current/python/bin/python 2 | 3 | from installer import Installer 4 | Installer().on_disk_change() 5 | -------------------------------------------------------------------------------- /hooks/storage-change.py: -------------------------------------------------------------------------------- 1 | import subprocess 2 | 3 | print(subprocess.check_output('snap run nextcloud.storage-change', shell=True)) 4 | -------------------------------------------------------------------------------- /meta/hooks/configure: -------------------------------------------------------------------------------- 1 | #!/snap/nextcloud/current/python/bin/python 2 | 3 | import os, sys 4 | sys.path.append(os.path.join(os.environ['SNAP'], 'hooks')) 5 | 6 | from installer import Installer 7 | Installer().configure() 8 | -------------------------------------------------------------------------------- /meta/hooks/install: -------------------------------------------------------------------------------- 1 | #!/snap/nextcloud/current/python/bin/python 2 | 3 | import os, sys 4 | sys.path.append(os.path.join(os.environ['SNAP'], 'hooks')) 5 | 6 | from installer import Installer 7 | Installer().install() 8 | -------------------------------------------------------------------------------- /meta/hooks/post-refresh: -------------------------------------------------------------------------------- 1 | #!/snap/nextcloud/current/python/bin/python 2 | 3 | import os, sys 4 | sys.path.append(os.path.join(os.environ['SNAP'], 'hooks')) 5 | 6 | from installer import Installer 7 | Installer().post_refresh() 8 | -------------------------------------------------------------------------------- /meta/hooks/pre-refresh: -------------------------------------------------------------------------------- 1 | #!/snap/nextcloud/current/python/bin/python 2 | 3 | import os, sys 4 | sys.path.append(os.path.join(os.environ['SNAP'], 'hooks')) 5 | 6 | from installer import Installer 7 | Installer().pre_refresh() 8 | -------------------------------------------------------------------------------- /meta/snap.yaml: -------------------------------------------------------------------------------- 1 | apps: 2 | postgresql: 3 | user: nextcloud 4 | command: bin/service.postgresql.sh start 5 | daemon: forking 6 | plugs: 7 | - network 8 | - network-bind 9 | restart-condition: always 10 | before: [php-fpm] 11 | 12 | redis: 13 | user: nextcloud 14 | daemon: simple 15 | command: bin/service.redis.sh 16 | restart-condition: always 17 | before: [php-fpm] 18 | 19 | php-fpm: 20 | user: nextcloud 21 | command: bin/service.php-fpm.sh start 22 | daemon: forking 23 | plugs: 24 | - network 25 | - network-bind 26 | restart-condition: always 27 | post-start-command: bin/service.php-fpm.sh post-start 28 | after: [postgresql] 29 | before: [nginx] 30 | 31 | nginx: 32 | command: bin/service.nginx.sh 33 | user: nextcloud 34 | daemon: simple 35 | plugs: 36 | - network 37 | - network-bind 38 | restart-condition: always 39 | 40 | occ: 41 | command: bin/occ-runner 42 | 43 | psql: 44 | command: bin/psql.sh 45 | 46 | pgdumpall: 47 | command: bin/pg_dumpall.sh 48 | 49 | cron: 50 | command: bin/nextcloud-cron 51 | 52 | php: 53 | command: bin/php-runner 54 | 55 | storage-change: 56 | command: hooks/storage-change 57 | 58 | access-change: 59 | command: hooks/access-change 60 | 61 | backup-pre-stop: 62 | command: hooks/backup-pre-stop 63 | 64 | restore-pre-start: 65 | command: hooks/restore-pre-start 66 | 67 | restore-post-start: 68 | command: hooks/restore-post-start 69 | 70 | confinement: strict 71 | description: Nextcloud 72 | grade: stable 73 | name: nextcloud 74 | summary: Nextcloud 75 | -------------------------------------------------------------------------------- /nginx/build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh -ex 2 | 3 | DIR=$( cd "$( dirname "$0" )" && pwd ) 4 | cd ${DIR} 5 | VERSION=$1 6 | BUILD_DIR=${DIR}/../build/snap/nginx 7 | while ! docker create --name=nginx nginx:$VERSION ; do 8 | sleep 1 9 | echo "retry docker" 10 | done 11 | mkdir -p ${BUILD_DIR} 12 | cd ${BUILD_DIR} 13 | docker export nginx -o app.tar 14 | tar xf app.tar 15 | rm -rf app.tar 16 | cp ${DIR}/nginx.sh ${BUILD_DIR}/bin/ 17 | -------------------------------------------------------------------------------- /nginx/nginx.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash -e 2 | DIR=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && cd .. && pwd ) 3 | LIBS=$(echo ${DIR}/lib/*-linux-gnu*) 4 | LIBS=$LIBS:$(echo ${DIR}/usr/lib/*-linux-gnu*) 5 | ${DIR}/lib/*-linux*/ld-*.so --library-path $LIBS ${DIR}/usr/sbin/nginx "$@" 6 | -------------------------------------------------------------------------------- /package.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash -ex 2 | 3 | DIR=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd ) 4 | cd ${DIR} 5 | 6 | if [[ -z "$2" ]]; then 7 | echo "usage $0 app version" 8 | exit 1 9 | fi 10 | 11 | NAME=$1 12 | VERSION=$2 13 | ARCH=$(dpkg --print-architecture) 14 | 15 | SNAP_DIR=${DIR}/build/snap 16 | 17 | apt update 18 | apt -y install squashfs-tools 19 | 20 | cp -r ${DIR}/bin ${SNAP_DIR} 21 | cp -r ${DIR}/config ${SNAP_DIR} 22 | cp -r ${DIR}/hooks ${SNAP_DIR} 23 | cp -r ${DIR}/meta ${SNAP_DIR} 24 | 25 | echo "version: $VERSION" >> ${SNAP_DIR}/meta/snap.yaml 26 | echo "architectures:" >> ${SNAP_DIR}/meta/snap.yaml 27 | echo "- ${ARCH}" >> ${SNAP_DIR}/meta/snap.yaml 28 | 29 | PACKAGE=${NAME}_${VERSION}_${ARCH}.snap 30 | echo ${PACKAGE} > ${DIR}/package.name 31 | mksquashfs ${SNAP_DIR} ${DIR}/${PACKAGE} -noappend -comp xz -no-xattrs -all-root 32 | mkdir ${DIR}/artifact 33 | cp ${DIR}/${PACKAGE} ${DIR}/artifact 34 | -------------------------------------------------------------------------------- /php/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM php:8.3.9-fpm-bullseye 2 | RUN apt-get update && apt-get install -y \ 3 | libfreetype6-dev \ 4 | libjpeg62-turbo-dev \ 5 | libpng-dev \ 6 | libzip-dev \ 7 | libsmbclient-dev \ 8 | libxml2-dev \ 9 | libsqlite3-dev \ 10 | libpq-dev \ 11 | libldap2-dev \ 12 | libsasl2-dev \ 13 | libfreetype6-dev \ 14 | liblqr-1-0-dev \ 15 | libfftw3-dev \ 16 | libjbig-dev \ 17 | libtiff5-dev \ 18 | libwebp-dev \ 19 | libmemcached-dev \ 20 | libmcrypt-dev \ 21 | zip \ 22 | wget \ 23 | unzip \ 24 | libgmp-dev \ 25 | libonig-dev \ 26 | libicu-dev \ 27 | libmagickwand-dev \ 28 | libbz2-dev \ 29 | git \ 30 | --no-install-recommends 31 | 32 | RUN mkdir -p /usr/src/php/ext/memcached 33 | WORKDIR /usr/src/php/ext/memcached 34 | RUN wget https://github.com/php-memcached-dev/php-memcached/archive/v3.1.5.zip; unzip /usr/src/php/ext/memcached/v*.zip 35 | RUN mv /usr/src/php/ext/memcached/php-memcached-*/* /usr/src/php/ext/memcached/ 36 | 37 | RUN docker-php-ext-install bz2 38 | RUN docker-php-ext-configure memcached 39 | RUN docker-php-ext-install memcached 40 | RUN docker-php-ext-install gmp 41 | #RUN pecl install imagick 42 | RUN git clone https://github.com/Imagick/imagick.git --depth 1 /tmp/imagick && \ 43 | cd /tmp/imagick && \ 44 | git fetch origin master && \ 45 | git switch master && \ 46 | cd /tmp/imagick && \ 47 | phpize && \ 48 | ./configure && \ 49 | make && \ 50 | make install && \ 51 | docker-php-ext-enable imagick 52 | 53 | RUN pecl install smbclient 54 | RUN pecl install apcu 55 | RUN pecl install mcrypt-1.0.7 56 | RUN pecl install redis 57 | RUN docker-php-ext-enable redis 58 | RUN docker-php-ext-configure intl 59 | RUN docker-php-ext-install intl 60 | RUN docker-php-ext-enable apcu 61 | RUN docker-php-ext-install ldap 62 | RUN docker-php-ext-enable mcrypt 63 | RUN docker-php-ext-install bcmath 64 | RUN docker-php-ext-install pdo_mysql 65 | RUN docker-php-ext-install mysqli 66 | RUN docker-php-ext-install mbstring 67 | RUN docker-php-ext-install opcache 68 | RUN docker-php-ext-install zip 69 | RUN docker-php-ext-install pcntl 70 | RUN docker-php-ext-install exif 71 | RUN docker-php-ext-install sysvsem 72 | #RUN docker-php-ext-enable imagick 73 | 74 | RUN docker-php-ext-enable smbclient 75 | RUN docker-php-ext-install pdo pdo_pgsql 76 | RUN docker-php-ext-configure gd --with-freetype --with-jpeg 77 | RUN docker-php-ext-install -j2 gd 78 | RUN rm -rf /var/lib/apt/lists/* 79 | RUN apt remove -y git 80 | 81 | 82 | 83 | -------------------------------------------------------------------------------- /php/build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh -xe 2 | 3 | DIR=$( cd "$( dirname "$0" )" && pwd ) 4 | cd ${DIR} 5 | 6 | BUILD_DIR=${DIR}/../build/snap/php 7 | 8 | docker ps -a -q --filter ancestor=php:syncloud --format="{{.ID}}" | xargs docker stop | xargs docker rm || true 9 | docker rmi php:syncloud || true 10 | docker build -t php:syncloud . 11 | docker run php:syncloud php -i 12 | docker create --name=php php:syncloud 13 | mkdir -p ${BUILD_DIR} 14 | cd ${BUILD_DIR} 15 | docker export php -o php.tar 16 | tar xf php.tar 17 | rm -rf php.tar 18 | mv ${BUILD_DIR}/usr/lib/*-linux*/ImageMagick-*/modules-*/coders ${BUILD_DIR}/usr/lib/ImageMagickCoders 19 | ls -la ${BUILD_DIR}/usr/lib/ImageMagickCoders 20 | cp ${DIR}/php.sh ${BUILD_DIR}/bin 21 | cp ${DIR}/php-fpm.sh ${BUILD_DIR}/bin 22 | mkdir -p ${BUILD_DIR}/lib/php/extensions 23 | mv ${BUILD_DIR}/usr/local/lib/php/extensions/*/*.so ${BUILD_DIR}/lib/php/extensions 24 | rm -rf ${BUILD_DIR}/usr/src 25 | -------------------------------------------------------------------------------- /php/php-fpm.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash -e 2 | DIR=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && cd .. && pwd ) 3 | LIBS=$(echo ${DIR}/lib/*-linux-gnu*) 4 | LIBS=$LIBS:$(echo ${DIR}/usr/lib/*-linux-gnu*) 5 | LIBS=$LIBS:$(echo ${DIR}/usr/lib) 6 | LIBS=$LIBS:$(echo ${DIR}/usr/lib/*-linux-gnu*/samba) 7 | MAGICK_CODER_MODULE_PATH=$(echo ${DIR}/usr/lib/ImageMagickCoders) PHP_INI_SCAN_DIR=${DIR}/usr/local/etc/php/conf.d ${DIR}/lib/*-linux*/ld-*.so --library-path $LIBS ${DIR}/usr/local/sbin/php-fpm "$@" 8 | -------------------------------------------------------------------------------- /php/php.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash -e 2 | DIR=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && cd .. && pwd ) 3 | LIBS=$(echo ${DIR}/lib/*-linux-gnu*) 4 | LIBS=$LIBS:$(echo ${DIR}/usr/lib/*-linux-gnu*) 5 | LIBS=$LIBS:$(echo ${DIR}/usr/lib) 6 | LIBS=$LIBS:$(echo ${DIR}/usr/lib/*-linux-gnu*/samba) 7 | MAGICK_CODER_MODULE_PATH=$(echo ${DIR}/usr/lib/ImageMagickCoders) PHP_INI_SCAN_DIR=${DIR}/usr/local/etc/php/conf.d ${DIR}/lib/*-linux*/ld-*.so --library-path $LIBS ${DIR}/usr/local/bin/php "$@" 8 | -------------------------------------------------------------------------------- /postgresql/Dockerfile: -------------------------------------------------------------------------------- 1 | ARG MAJOR_VERSION 2 | FROM postgres:$MAJOR_VERSION-bullseye -------------------------------------------------------------------------------- /postgresql/bin/initdb.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash -e 2 | DIR=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && cd .. && pwd ) 3 | LIBS=$(echo ${DIR}/lib/*linux*/) 4 | LIBS=$LIBS:$(echo ${DIR}/usr/lib/*linux*) 5 | exec ${DIR}/lib/*/ld-*.so --library-path $LIBS ${DIR}/usr/lib/postgresql/*/bin/initdb "$@" 6 | -------------------------------------------------------------------------------- /postgresql/bin/pg_ctl.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash -e 2 | DIR=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && cd .. && pwd ) 3 | LIBS=$(echo ${DIR}/lib/*linux*/) 4 | LIBS=$LIBS:$(echo ${DIR}/usr/lib/*linux*) 5 | exec ${DIR}/lib/*/ld-*.so --library-path $LIBS ${DIR}/usr/lib/postgresql/*/bin/pg_ctl "$@" 6 | -------------------------------------------------------------------------------- /postgresql/bin/pg_dumpall.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash -e 2 | DIR=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && cd .. && pwd ) 3 | LIBS=$(echo ${DIR}/lib/*linux*/) 4 | LIBS=$LIBS:$(echo ${DIR}/usr/lib/*linux*) 5 | exec ${DIR}/lib/*/ld-*.so --library-path $LIBS ${DIR}/usr/lib/postgresql/*/bin/pg_dumpall "$@" 6 | -------------------------------------------------------------------------------- /postgresql/bin/psql.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash -e 2 | DIR=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && cd .. && pwd ) 3 | LIBS=$(echo ${DIR}/lib/*linux*/) 4 | LIBS=$LIBS:$(echo ${DIR}/usr/lib/*linux*) 5 | exec ${DIR}/lib/*/ld-*.so --library-path $LIBS ${DIR}/usr/lib/postgresql/*/bin/psql "$@" 6 | -------------------------------------------------------------------------------- /postgresql/build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh -ex 2 | 3 | DIR=$( cd "$( dirname "$0" )" && pwd ) 4 | cd ${DIR} 5 | 6 | MAJOR_VERSION=16 7 | 8 | BUILD_DIR=${DIR}/../build/snap/postgresql 9 | 10 | docker ps -a -q --filter ancestor=postgres:syncloud --format="{{.ID}}" | xargs docker stop | xargs docker rm || true 11 | docker rmi postgres:syncloud || true 12 | docker build --build-arg MAJOR_VERSION=$MAJOR_VERSION -t postgres:syncloud . 13 | docker run postgres:syncloud postgres --help 14 | docker create --name=postgres postgres:syncloud 15 | mkdir -p ${BUILD_DIR} 16 | cd ${BUILD_DIR} 17 | echo "${MAJOR_VERSION}" > ${BUILD_DIR}/../db.major.version 18 | docker export postgres -o postgres.tar 19 | tar xf postgres.tar 20 | rm -rf postgres.tar 21 | ls -la 22 | ls -la bin 23 | ls -la usr/bin 24 | ls -ls usr/share/postgresql-common/pg_wrapper 25 | PGBIN=$(echo usr/lib/postgresql/*/bin) 26 | ldd $PGBIN/initdb || true 27 | mv $PGBIN/postgres $PGBIN/postgres.bin 28 | mv $PGBIN/pg_dump $PGBIN/pg_dump.bin 29 | cp $DIR/bin/* bin 30 | cp $DIR/pgbin/* $PGBIN 31 | -------------------------------------------------------------------------------- /postgresql/pgbin/pg_dump: -------------------------------------------------------------------------------- 1 | #!/bin/bash -e 2 | DIR=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && cd ../../../../.. && pwd ) 3 | LIBS=$(echo ${DIR}/lib/*linux*/) 4 | LIBS=$LIBS:$(echo ${DIR}/usr/lib/*linux*) 5 | exec ${DIR}/lib/*/ld-*.so --library-path $LIBS ${DIR}/usr/lib/postgresql/*/bin/pg_dump.bin "$@" 6 | -------------------------------------------------------------------------------- /postgresql/pgbin/postgres: -------------------------------------------------------------------------------- 1 | #!/bin/bash -e 2 | DIR=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && cd ../../../../.. && pwd ) 3 | LIBS=$(echo ${DIR}/lib/*linux*/) 4 | LIBS=$LIBS:$(echo ${DIR}/usr/lib/*linux*) 5 | exec ${DIR}/lib/*/ld-*.so --library-path $LIBS ${DIR}/usr/lib/postgresql/*/bin/postgres.bin "$@" 6 | -------------------------------------------------------------------------------- /python/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:3.8-slim-buster 2 | COPY requirements.txt / 3 | RUN pip install -r /requirements.txt 4 | RUN rm -rf /var/lib/apt/lists/* -------------------------------------------------------------------------------- /python/build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh -ex 2 | 3 | DIR=$( cd "$( dirname "$0" )" && pwd ) 4 | cd ${DIR} 5 | 6 | BUILD_DIR=${DIR}/../build/snap/python 7 | docker ps -a -q --filter ancestor=python:syncloud --format="{{.ID}}" | xargs docker stop | xargs docker rm || true 8 | docker rmi python:syncloud || true 9 | docker build -t python:syncloud . 10 | docker run python:syncloud python --help 11 | docker create --name=python python:syncloud 12 | mkdir -p ${BUILD_DIR} 13 | cd ${BUILD_DIR} 14 | docker export python -o python.tar 15 | tar xf python.tar 16 | rm -rf python.tar 17 | cp ${DIR}/python ${BUILD_DIR}/bin/ 18 | ls -la ${BUILD_DIR}/bin 19 | rm -rf ${BUILD_DIR}/usr/src 20 | -------------------------------------------------------------------------------- /python/python: -------------------------------------------------------------------------------- 1 | #!/bin/bash -e 2 | DIR=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && cd .. && pwd ) 3 | LIBS=$(echo ${DIR}/lib/*-linux-gnu*) 4 | LIBS=$LIBS:$(echo ${DIR}/lib/*-linux-gnu*) 5 | LIBS=$LIBS:$(echo ${DIR}/usr/lib/*-linux-gnu*) 6 | LIBS=$LIBS:$(echo ${DIR}/usr/local/lib) 7 | ${DIR}/lib/*-linux*/ld-*.so --library-path $LIBS ${DIR}/usr/local/bin/python3 "$@" 8 | -------------------------------------------------------------------------------- /python/requirements.txt: -------------------------------------------------------------------------------- 1 | beautifulsoup4==4.4.0 2 | requests-unixsocket==0.1.5 3 | massedit==0.67.1 4 | python-crontab==1.7.2 5 | syncloud-lib==278 6 | -------------------------------------------------------------------------------- /redis/bin/redis.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash -e 2 | DIR=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && cd .. && pwd ) 3 | LIBS=$(echo ${DIR}/lib/*-linux-gnu*) 4 | exec ${DIR}/lib/*-linux*/ld-*.so.* --library-path $LIBS ${DIR}/usr/local/bin/redis-server "$@" -------------------------------------------------------------------------------- /redis/build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh -ex 2 | 3 | DIR=$( cd "$( dirname "$0" )" && pwd ) 4 | cd ${DIR} 5 | BUILD_DIR=${DIR}/../build/snap/redis 6 | mkdir $BUILD_DIR 7 | cp -r /usr ${BUILD_DIR} 8 | cp -r /lib ${BUILD_DIR} 9 | cp -r ${DIR}/bin ${BUILD_DIR}/bin 10 | -------------------------------------------------------------------------------- /redis/test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh -ex 2 | 3 | DIR=$( cd "$( dirname "$0" )" && pwd ) 4 | cd ${DIR} 5 | BUILD_DIR=${DIR}/../build/snap/redis 6 | $BUILD_DIR/bin/redis.sh -v 7 | -------------------------------------------------------------------------------- /test/.gitignore: -------------------------------------------------------------------------------- 1 | screenshot 2 | firefox 3 | *.xpi 4 | debug.py 5 | *.png -------------------------------------------------------------------------------- /test/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/syncloud/nextcloud/bf69613b595b1415457ab11139c69b350469139b/test/__init__.py -------------------------------------------------------------------------------- /test/conftest.py: -------------------------------------------------------------------------------- 1 | from os.path import dirname, join 2 | from syncloudlib.integration.conftest import * 3 | 4 | DIR = dirname(__file__) 5 | 6 | 7 | @pytest.fixture(scope="session") 8 | def project_dir(): 9 | return join(DIR, '..') 10 | -------------------------------------------------------------------------------- /test/deps.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash -e 2 | 3 | apt-get update 4 | apt-get install -y sshpass openssh-client netcat rustc file libxml2-dev libxslt-dev build-essential libz-dev curl 5 | pip install -r requirements.txt 6 | -------------------------------------------------------------------------------- /test/requirements.txt: -------------------------------------------------------------------------------- 1 | pytest==6.2.4 2 | selenium==4.21.0 3 | syncloud-lib==319 4 | 5 | -------------------------------------------------------------------------------- /test/test.odt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/syncloud/nextcloud/bf69613b595b1415457ab11139c69b350469139b/test/test.odt -------------------------------------------------------------------------------- /test/test.py: -------------------------------------------------------------------------------- 1 | import os 2 | import json 3 | import pytest 4 | import requests 5 | import shutil 6 | from os.path import join 7 | from requests.auth import HTTPBasicAuth 8 | from requests.packages.urllib3.exceptions import InsecureRequestWarning 9 | from subprocess import check_output 10 | from syncloudlib.integration.hosts import add_host_alias 11 | from syncloudlib.integration.installer import local_install, wait_for_installer 12 | from syncloudlib.integration.loop import loop_device_add, loop_device_cleanup 13 | from syncloudlib.http import wait_for_response 14 | 15 | TMP_DIR = '/tmp/syncloud' 16 | 17 | requests.packages.urllib3.disable_warnings(InsecureRequestWarning) 18 | 19 | 20 | @pytest.fixture(scope="session") 21 | def module_setup(request, device, platform_data_dir, app_dir, artifact_dir): 22 | def module_teardown(): 23 | platform_log_dir = join(artifact_dir, 'platform_log') 24 | os.mkdir(platform_log_dir) 25 | device.scp_from_device('{0}/log/*'.format(platform_data_dir), platform_log_dir) 26 | device.run_ssh('ls -la /var/snap/nextcloud/current/nextcloud/config > {0}/config.ls.log'.format(TMP_DIR), throw=False) 27 | device.run_ssh('cp /var/snap/nextcloud/current/nextcloud/config/config.php {0}'.format(TMP_DIR), throw=False) 28 | device.run_ssh('snap run nextcloud.occ > {1}/occ.help.log'.format(app_dir, TMP_DIR), throw=False) 29 | device.run_ssh('top -bn 1 -w 500 -c > {0}/top.log'.format(TMP_DIR), throw=False) 30 | device.run_ssh('ps auxfw > {0}/ps.log'.format(TMP_DIR), throw=False) 31 | device.run_ssh('systemctl status snap.nextcloud.php-fpm > {0}/nextcloud.php-fpm.status.log'.format(TMP_DIR), 32 | throw=False) 33 | device.run_ssh('netstat -nlp > {0}/netstat.log'.format(TMP_DIR), throw=False) 34 | device.run_ssh('journalctl | tail -1000 > {0}/journalctl.log'.format(TMP_DIR), throw=False) 35 | device.run_ssh('ls -la /snap > {0}/snap.ls.log'.format(TMP_DIR), throw=False) 36 | device.run_ssh('ls -la /snap/nextcloud > {0}/snap.nextcloud.ls.log'.format(TMP_DIR), throw=False) 37 | device.run_ssh('ls -la /var/snap > {0}/var.snap.ls.log'.format(TMP_DIR), throw=False) 38 | device.run_ssh('ls -la /var/snap/nextcloud > {0}/var.snap.nextcloud.ls.log'.format(TMP_DIR), throw=False) 39 | device.run_ssh('ls -la /var/snap/nextcloud/current/ > {0}/var.snap.nextcloud.current.ls.log'.format(TMP_DIR), throw=False) 40 | device.run_ssh('ls -la /var/snap/nextcloud/current/nextcloud > {0}/var.snap.nextcloud.current.nextcloud.ls.log'.format(TMP_DIR), throw=False) 41 | device.run_ssh('ls -la /snap/nextcloud/current/nextcloud > {0}/snap.nextcloud.current.nextcloud.ls.log'.format(TMP_DIR), throw=False) 42 | device.run_ssh('ls -la /var/snap/nextcloud/common > {0}/var.snap.nextcloud.common.ls.log'.format(TMP_DIR), 43 | throw=False) 44 | device.run_ssh('ls -la /data > {0}/data.ls.log'.format(TMP_DIR), throw=False) 45 | device.run_ssh('ls -la /data/nextcloud > {0}/data.nextcloud.ls.log'.format(TMP_DIR), throw=False) 46 | 47 | app_log_dir = join(artifact_dir, 'log') 48 | os.mkdir(app_log_dir) 49 | device.scp_from_device('/var/snap/nextcloud/common/log/*.log', app_log_dir) 50 | device.scp_from_device('{0}/*'.format(TMP_DIR), app_log_dir) 51 | shutil.copy2('/etc/hosts', app_log_dir) 52 | check_output('chmod -R a+r {0}'.format(artifact_dir), shell=True) 53 | 54 | request.addfinalizer(module_teardown) 55 | 56 | 57 | def test_start(module_setup, device, device_host, app, domain): 58 | add_host_alias(app, device_host, domain) 59 | device.run_ssh('date', retries=100) 60 | device.run_ssh('mkdir {0}'.format(TMP_DIR)) 61 | 62 | 63 | def test_activate_device(device): 64 | response = device.activate_custom() 65 | assert response.status_code == 200, response.text 66 | 67 | 68 | def test_install(app_archive_path, device_session, device_host, device_password): 69 | local_install(device_host, device_password, app_archive_path) 70 | 71 | 72 | # noinspection PyUnresolvedReferences 73 | @pytest.mark.parametrize("megabytes", [1, 50]) 74 | def test_sync(app_domain, megabytes, device, device_user, device_password): 75 | sync_file = 'test.file-{0}'.format(megabytes) 76 | if os.path.isfile(sync_file): 77 | os.remove(sync_file) 78 | print(check_output('dd if=/dev/zero of={0} count={1} bs=1M'.format(sync_file, megabytes), shell=True)) 79 | webdav_upload(device_user, device_password, sync_file, sync_file, app_domain) 80 | 81 | sync_file_download = 'test.file.download' 82 | if os.path.isfile(sync_file_download): 83 | os.remove(sync_file_download) 84 | webdav_download(device_user, device_password, sync_file, sync_file_download, app_domain) 85 | 86 | assert os.path.isfile(sync_file_download) 87 | device.run_ssh('rm /data/nextcloud/{0}/files/{1}'.format(device_user, sync_file)) 88 | files_scan(device) 89 | 90 | 91 | def webdav_upload(user, password, file_from, file_to, app_domain): 92 | print(check_output('curl -k -T {2} https://{0}:{1}@{4}/remote.php/webdav/{3}'.format(user, password, file_from, file_to, 93 | app_domain), shell=True)) 94 | 95 | 96 | def webdav_download(user, password, file_from, file_to, app_domain): 97 | print(check_output('curl -k -o {3} https://{0}:{1}@{4}/remote.php/webdav/{2}'.format(user, password, file_from, file_to, 98 | app_domain), shell=True)) 99 | 100 | 101 | def files_scan(device): 102 | device.run_ssh('snap run nextcloud.occ files:scan --all') 103 | 104 | 105 | def test_occ(device): 106 | device.run_ssh('snap run nextcloud.occ') 107 | 108 | 109 | def test_psql_oc_ldap_group_mapping(device): 110 | device.run_ssh("snap run nextcloud.psql -c 'select * from oc_ldap_group_mapping' > {0}/psql.oc_ldap_group_mapping.log".format(TMP_DIR)) 111 | 112 | 113 | def test_cron(device): 114 | device.run_ssh('snap run nextcloud.cron') 115 | 116 | 117 | def test_visible_through_platform(app_domain): 118 | response = requests.get('https://{0}'.format(app_domain), verify=False) 119 | assert response.status_code == 200, response.text 120 | 121 | 122 | def test_occ_users(device): 123 | device.run_ssh('snap run nextcloud.occ user:list') 124 | 125 | 126 | def test_occ_check(device): 127 | device.run_ssh('snap run nextcloud.occ check') 128 | 129 | 130 | def test_occ_status(device): 131 | device.run_ssh('snap run nextcloud.occ status') 132 | 133 | 134 | def test_webdav(app_domain, artifact_dir, device_user, device_password): 135 | response = requests.request('PROPFIND', 'https://{0}:{1}@{2}/remote.php/webdav/'.format( 136 | device_user, device_password, app_domain), verify=False) 137 | with open(join(artifact_dir, 'webdav.list.log'), 'w') as f: 138 | f.write(str(response.text).replace(',', '\n')) 139 | 140 | 141 | def test_carddav(app_domain, artifact_dir, device_user, device_password): 142 | response = requests.request( 143 | 'PROPFIND', 144 | 'https://{0}/.well-known/carddav'.format(app_domain), 145 | allow_redirects=True, 146 | verify=False, 147 | auth=HTTPBasicAuth(device_user, device_password)) 148 | with open(join(artifact_dir, 'well-known.carddav.headers.log'), 'w') as f: 149 | f.write(str(response.headers).replace(',', '\n')) 150 | 151 | 152 | def test_caldav(app_domain, artifact_dir, device_user, device_password): 153 | response = requests.request( 154 | 'PROPFIND', 155 | 'https://{0}/.well-known/caldav'.format(app_domain), 156 | allow_redirects=True, 157 | verify=False, 158 | auth=HTTPBasicAuth(device_user, device_password)) 159 | with open(join(artifact_dir, 'well-known.caldav.headers.log'), 'w') as f: 160 | f.write(str(response.headers).replace(',', '\n')) 161 | 162 | 163 | def test_relative_redirect(app_domain, artifact_dir, device_user, device_password): 164 | response = requests.get( 165 | 'https://{0}/apps/files'.format(app_domain), 166 | allow_redirects=False, 167 | verify=False) 168 | assert response.headers['Location'] == '/apps/files/' 169 | 170 | 171 | def test_disk(app_domain, device, domain, device_user, device_password, artifact_dir): 172 | loop_device_cleanup(domain, '/tmp/test0', device_password) 173 | loop_device_cleanup(domain, '/tmp/test1', device_password) 174 | 175 | __create_test_dir('test00', app_domain, device_user, device_password, artifact_dir) 176 | files_scan(device) 177 | __check_test_dir(device_user, device_password, 'test00', app_domain, artifact_dir) 178 | 179 | device0 = loop_device_add(domain, 'ext4', '/tmp/test0', device_password) 180 | __activate_disk(device0, device, domain) 181 | __create_test_dir('test0', app_domain, device_user, device_password, artifact_dir) 182 | __check_test_dir(device_user, device_password, 'test0', app_domain, artifact_dir) 183 | 184 | device1 = loop_device_add(domain, 'ext2', '/tmp/test1', device_password) 185 | __activate_disk(device1, device, domain) 186 | __create_test_dir('test1', app_domain, device_user, device_password, artifact_dir) 187 | __check_test_dir(device_user, device_password, 'test1', app_domain, artifact_dir) 188 | 189 | __activate_disk(device0, device, domain) 190 | __check_test_dir(device_user, device_password, 'test0', app_domain, artifact_dir) 191 | 192 | __deactivate_disk(device, domain) 193 | 194 | 195 | def __log_data_dir(device): 196 | device.run_ssh('ls -la /data') 197 | device.run_ssh('mount') 198 | device.run_ssh('ls -la /data/') 199 | device.run_ssh('ls -la /data/nextcloud') 200 | 201 | 202 | def __activate_disk(loop_device, device, domain): 203 | __log_data_dir(device) 204 | session = device.login() 205 | response = session.post('https://{0}/rest/storage/activate/disk'.format(domain), 206 | json={'devices': [loop_device]}, allow_redirects=False, verify=False) 207 | assert response.status_code == 200, response.text 208 | 209 | wait_for_response(session, 'https://{0}/rest/job/status'.format(domain), 210 | lambda r: json.loads(r.text)['data']['status'] == 'Idle', 211 | attempts=100) 212 | 213 | __log_data_dir(device) 214 | files_scan(device) 215 | device.run_ssh('snap run nextcloud.occ > {0}/occ.activate.log'.format(TMP_DIR)) 216 | 217 | 218 | def __deactivate_disk(device, domain): 219 | response = device.login().post('https://{0}/rest/storage/deactivate'.format(domain), 220 | allow_redirects=False, verify=False) 221 | files_scan(device) 222 | assert response.status_code == 200, response.text 223 | 224 | 225 | def __create_test_dir(test_dir, app_domain, device_user, device_password, artifact_dir): 226 | response = requests.request('MKCOL', 'https://{0}:{1}@{2}/remote.php/webdav/{3}'.format( 227 | device_user, device_password, app_domain, test_dir), verify=False) 228 | with open(join(artifact_dir, 'create.{0}.dir.log'.format(test_dir)), 'w') as f: 229 | f.write(response.text) 230 | assert response.status_code == 201, response.text 231 | 232 | 233 | def __check_test_dir(device_user, device_password, test_dir, app_domain, artifact_dir): 234 | response = requests.request('PROPFIND', 'https://{0}:{1}@{2}/remote.php/webdav/'.format( 235 | device_user, device_password, app_domain), verify=False) 236 | with open(join(artifact_dir, 'check.{0}.dir.log'.format(test_dir)), 'w') as f: 237 | f.write(response.text) 238 | #dirs = map(lambda v: v['name'], info['data']['files']) 239 | assert test_dir in response.text, response.text 240 | 241 | 242 | def test_phpinfo(device): 243 | device.run_ssh('snap run nextcloud.php -i > {0}/phpinfo.log'.format(TMP_DIR)) 244 | 245 | 246 | def test_php_dns(device): 247 | ip = device.run_ssh('snap run nextcloud.php -r \\\"echo gethostbyname(\'apps.nextcloud.com\');\\\"') 248 | assert ip != "apps.nextcloud.com" 249 | 250 | 251 | def test_storage_change_event(device): 252 | device.run_ssh('snap run nextcloud.storage-change > {0}/storage-change.log'.format(TMP_DIR)) 253 | 254 | 255 | def test_access_change_event(device): 256 | device.run_ssh('snap run nextcloud.access-change > {0}/access-change.log'.format(TMP_DIR)) 257 | 258 | 259 | def test_remove(device, app): 260 | response = device.app_remove(app) 261 | assert response.status_code == 200, response.text 262 | 263 | 264 | def test_reinstall(app_archive_path, device_host, device_password): 265 | local_install(device_host, device_password, app_archive_path) 266 | 267 | 268 | def test_upgrade(app_archive_path, device_host, device_password): 269 | local_install(device_host, device_password, app_archive_path) 270 | 271 | 272 | def test_upgrade_from_store(device, app, app_archive_path, device_host, device_password): 273 | response = device.app_remove(app) 274 | assert response.status_code == 200, response.text 275 | response = device.app_install(app) 276 | assert response.status_code == 200, response.text 277 | local_install(device_host, device_password, app_archive_path) 278 | 279 | 280 | def test_install_calendar(device): 281 | device.run_ssh('snap run nextcloud.occ app:install calendar', retries=10, sleep=10) 282 | 283 | 284 | def test_install_contacts(device): 285 | device.run_ssh('snap run nextcloud.occ app:install contacts', retries=10, sleep=10) 286 | 287 | 288 | def test_install_office(device, arch): 289 | device.run_ssh('snap run nextcloud.occ app:install richdocuments', retries=10, sleep=10) 290 | 291 | 292 | def test_setupchecks(device, artifact_dir): 293 | output = device.run_ssh('snap run nextcloud.occ setupchecks --output=json_pretty', throw=False) 294 | with open(join(artifact_dir, 'setupchecks.log'), 'w') as f: 295 | f.write(output) 296 | invalid = [] 297 | jout = json.loads(output) 298 | for key in jout.keys(): 299 | for name in jout[key].keys(): 300 | if jout[key][name]['severity'] == 'error': 301 | print(jout[key][name]) 302 | invalid.append(jout[key][name]) 303 | 304 | assert len(invalid) == 0 305 | 306 | 307 | def test_upload_office_file(device, arch, device_user, device_password, app_domain): 308 | if arch == "arm": 309 | webdav_upload(device_user, device_password, 'test.odt', 'test.odt', app_domain) 310 | files_scan(device) 311 | -------------------------------------------------------------------------------- /test/ui.py: -------------------------------------------------------------------------------- 1 | from os.path import dirname, join 2 | from subprocess import check_output 3 | 4 | import pytest 5 | from selenium.webdriver.common.by import By 6 | from selenium.webdriver.common.keys import Keys 7 | from selenium.webdriver.support import expected_conditions as EC 8 | from syncloudlib.integration.hosts import add_host_alias 9 | 10 | DIR = dirname(__file__) 11 | TMP_DIR = '/tmp/syncloud/ui' 12 | 13 | 14 | @pytest.fixture(scope="session") 15 | def module_setup(request, device, artifact_dir, ui_mode, selenium): 16 | def module_teardown(): 17 | device.activated() 18 | device.run_ssh('mkdir -p {0}'.format(TMP_DIR), throw=False) 19 | device.run_ssh('journalctl > {0}/journalctl.ui.{1}.log'.format(TMP_DIR, ui_mode), throw=False) 20 | device.scp_from_device('{0}/*'.format(TMP_DIR), join(artifact_dir, 'log')) 21 | check_output('cp /videos/* {0}'.format(artifact_dir), shell=True) 22 | check_output('chmod -R a+r {0}'.format(artifact_dir), shell=True) 23 | selenium.log() 24 | request.addfinalizer(module_teardown) 25 | 26 | 27 | def test_start(module_setup, app, domain, device_host): 28 | add_host_alias(app, device_host, domain) 29 | 30 | 31 | def test_login(selenium, device_user, device_password): 32 | selenium.open_app() 33 | selenium.find_by_id("user").send_keys(device_user) 34 | password = selenium.find_by_id("password") 35 | password.send_keys(device_password) 36 | selenium.screenshot('login') 37 | password.send_keys(Keys.RETURN) 38 | #selenium.find_by_xpath("//span[contains(.,'Continue with this unsupported browser')]").click() 39 | wizard_close_button = selenium.find_by_xpath('//div[contains(@class, "first-run-wizard")]//div[@class="modal-container__content"]//button[@aria-label="Close"]') 40 | #wizard_close_button = selenium.find_by_xpath('//button[contains(@class, "close-button")]') 41 | selenium.screenshot('main_first_time') 42 | # hover = ActionChains(selenium.driver).move_to_element(wizard_close_button) 43 | # hover.perform() 44 | # selenium.screenshot('main_first_time-hover') 45 | # selenium.wait_driver.until(EC.element_to_be_clickable((By.CSS_SELECTOR, close_css_selector))) 46 | # selenium.screenshot('main_first_time-click') 47 | wizard_close_button.click() 48 | 49 | selenium.screenshot('main') 50 | 51 | 52 | def test_settings(selenium, app_domain): 53 | selenium.driver.get("https://{0}/settings/admin".format(app_domain)) 54 | selenium.find_by_xpath("//h2[contains(.,'Background jobs')]") 55 | selenium.screenshot('admin') 56 | 57 | 58 | def test_settings_user(selenium, app_domain): 59 | selenium.driver.get("https://{0}/settings/user".format(app_domain)) 60 | selenium.find_by_xpath("//label[contains(.,'Profile picture')]") 61 | selenium.screenshot('user') 62 | 63 | 64 | def test_settings_ldap(selenium, app_domain): 65 | selenium.driver.get("https://{0}/settings/admin/ldap".format(app_domain)) 66 | selenium.find_by_xpath("//h2[text()='LDAP/AD integration']") 67 | selenium.screenshot('admin-ldap') 68 | 69 | 70 | def test_settings_security(selenium, app_domain): 71 | selenium.driver.get("https://{0}/settings/admin/overview#security-warning".format(app_domain)) 72 | selenium.find_by_xpath("//h2[text()='Security & setup warnings']") 73 | progress_xpath = "//span[text()='Checking for system and security issues.']" 74 | selenium.find_by_xpath(progress_xpath) 75 | selenium.wait_or_screenshot(EC.invisibility_of_element_located((By.XPATH, progress_xpath))) 76 | source = selenium.driver.page_source 77 | selenium.screenshot('admin-security') 78 | assert 'no SVG support' not in source 79 | 80 | 81 | # def test_settings_additional(selenium, app_domain): 82 | # selenium.driver.get("https://{0}/settings/admin/additional".format(app_domain)) 83 | # selenium.find_by_xpath("//h2[text()='Maps routing settings']") 84 | # selenium.screenshot('admin-additional') 85 | 86 | 87 | # def test_apps_calendar(selenium, app_domain): 88 | # selenium.driver.get("https://{0}/calendar".format(app_domain)) 89 | # selenium.find_by_xpath("//span[@text()='+ New calendar']") 90 | # selenium.screenshot('calendar') 91 | 92 | 93 | def test_verification(selenium, app_domain): 94 | selenium.driver.get('https://{0}/settings/integrity/failed'.format(app_domain)) 95 | selenium.find_by_xpath("//pre[text()='No errors have been found.']") 96 | selenium.screenshot('integrity-failed') 97 | source = selenium.driver.page_source 98 | assert 'INVALID_HASH' not in source 99 | assert 'EXCEPTION' not in source 100 | 101 | 102 | def test_users(selenium, app_domain, ui_mode): 103 | selenium.driver.get('https://{0}/settings/users'.format(app_domain)) 104 | if ui_mode == "desktop": 105 | selenium.find_by_xpath("//a[@title='Admins']") 106 | selenium.screenshot('users') 107 | source = selenium.driver.page_source 108 | assert 'Server Error' not in source 109 | 110 | def test_office(selenium, app_domain): 111 | selenium.driver.get('https://{0}/settings/admin/richdocuments'.format(app_domain)) 112 | selenium.find_by_xpath("//label[normalize-space(text())='Use your own server']").click() 113 | selenium.screenshot('office-own') 114 | url = selenium.find_by_xpath("//input[@id='wopi_url']") 115 | url.clear() 116 | url.send_keys("https://{0}".format(app_domain)) 117 | selenium.find_by_xpath("//*[normalize-space(text())='Disable certificate verification (insecure)']").click() 118 | selenium.screenshot('office-own-url') 119 | selenium.find_by_xpath("//input[@value='Save']").click() 120 | #selenium.find_by_xpath("//span[normalize-space(text())='Collabora Online server is reachable.']") 121 | selenium.screenshot('office-status') 122 | 123 | 124 | def test_app_install(selenium, app_domain): 125 | selenium.driver.get('https://{0}/settings/apps/discover/memories'.format(app_domain)) 126 | selenium.find_by(By.XPATH, "//input[@value='Download and enable']").click() 127 | assert not selenium.exists_by(By.XPATH, "//div[contains(.,'Error')]") 128 | selenium.screenshot('install-app') 129 | -------------------------------------------------------------------------------- /test/upgrade.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | from subprocess import check_output, run 3 | from syncloudlib.integration.hosts import add_host_alias 4 | from syncloudlib.integration.installer import local_install 5 | from syncloudlib.http import wait_for_rest 6 | import requests 7 | 8 | TMP_DIR = '/tmp/syncloud' 9 | 10 | 11 | @pytest.fixture(scope="session") 12 | def module_setup(request, device, artifact_dir): 13 | def module_teardown(): 14 | device.run_ssh('journalctl > {0}/refresh.journalctl.log'.format(TMP_DIR), throw=False) 15 | device.scp_from_device('{0}/*'.format(TMP_DIR), artifact_dir) 16 | run('cp /videos/* {0}'.format(artifact_dir), shell=True) 17 | check_output('chmod -R a+r {0}'.format(artifact_dir), shell=True) 18 | 19 | request.addfinalizer(module_teardown) 20 | 21 | 22 | def test_start(module_setup, app, device_host, domain, device): 23 | add_host_alias(app, device_host, domain) 24 | device.activated() 25 | device.run_ssh('rm -rf {0}'.format(TMP_DIR), throw=False) 26 | device.run_ssh('mkdir {0}'.format(TMP_DIR), throw=False) 27 | 28 | 29 | def test_upgrade(device, device_user, device_password, device_host, app_archive_path, app_domain, app_dir): 30 | device.run_ssh('snap remove nextcloud') 31 | device.run_ssh('snap install nextcloud', retries=10) 32 | local_install(device_host, device_password, app_archive_path) 33 | wait_for_rest(requests.session(), "https://{0}".format(app_domain), 200, 10) 34 | 35 | --------------------------------------------------------------------------------