Compare commits
234 Commits
devtools
...
zero-downt
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
946a9de553 | ||
|
|
bf004661b0 | ||
|
|
9cb21ffb8a | ||
|
|
375928575c | ||
|
|
36a5bdda1e | ||
|
|
d0b88ace16 | ||
|
|
2e74db8684 | ||
|
|
597844a483 | ||
|
|
3c934fd394 | ||
|
|
c66d4bd7d7 | ||
|
|
5deb6fb09c | ||
|
|
55dfc429cb | ||
|
|
5a9e2751db | ||
|
|
9e6f17cf1e | ||
|
|
e7c95ea2e4 | ||
|
|
16815980a9 | ||
|
|
d49982299d | ||
|
|
cf7ecd5f4f | ||
|
|
406a57d203 | ||
|
|
a0c58111ec | ||
|
|
355ceb2517 | ||
|
|
c4d2d74f70 | ||
|
|
058bf95ed6 | ||
|
|
adf0b55548 | ||
|
|
7b76a8a562 | ||
|
|
f6501aff2c | ||
|
|
0101fc58f0 | ||
|
|
36e25d3b7e | ||
|
|
c36ad11f17 | ||
|
|
a19a7faf43 | ||
|
|
9f8e3f3bd5 | ||
|
|
6784d3c31f | ||
|
|
8a253fe371 | ||
|
|
03517e3037 | ||
|
|
ec71062edb | ||
|
|
8d82de705f | ||
|
|
84f58908ed | ||
|
|
6042b5c086 | ||
|
|
0eaea89433 | ||
|
|
cca9825c1c | ||
|
|
59747d5a60 | ||
|
|
3883b0b337 | ||
|
|
c41d61f17a | ||
|
|
f9bde4b1e2 | ||
|
|
5168068e7f | ||
|
|
d2acf47492 | ||
|
|
b8358fc949 | ||
|
|
52c92afd68 | ||
|
|
bb4cd9e297 | ||
|
|
0a37bcbba9 | ||
|
|
64c61b74e4 | ||
|
|
58cd6f04dd | ||
|
|
78610bb86b | ||
|
|
cb69d89ca5 | ||
|
|
88f18e1d3d | ||
|
|
10e8867d4b | ||
|
|
d66a38943f | ||
|
|
18a6de19d4 | ||
|
|
b2c67f3d10 | ||
|
|
a359a8d3f2 | ||
|
|
273111b0c6 | ||
|
|
46fe09352e | ||
|
|
ab79a6be97 | ||
|
|
c4a70d8865 | ||
|
|
bcd975e02f | ||
|
|
7fd1bdc29f | ||
|
|
4b69588894 | ||
|
|
88936def38 | ||
|
|
caf96dd931 | ||
|
|
6b13e04aea | ||
|
|
a9795c3298 | ||
|
|
ecb2634a77 | ||
|
|
237a5a6441 | ||
|
|
674d4feb17 | ||
|
|
610906f891 | ||
|
|
258f7fb137 | ||
|
|
475ef47881 | ||
|
|
39c28dddf0 | ||
|
|
2b8664e49e | ||
|
|
d0c5706133 | ||
|
|
cd45701470 | ||
|
|
070ad968e6 | ||
|
|
c29993b416 | ||
|
|
a481a48992 | ||
|
|
3be0e85a77 | ||
|
|
973cb7a081 | ||
|
|
33dacb36de | ||
|
|
aaa07d4c70 | ||
|
|
23cd4e4da7 | ||
|
|
43b9c83917 | ||
|
|
b558643405 | ||
|
|
acdf98e71b | ||
|
|
c30b27b240 | ||
|
|
dbdd4d20cb | ||
|
|
379f0d6799 | ||
|
|
8e299cf819 | ||
|
|
14536868c5 | ||
|
|
bbca460ed2 | ||
|
|
cd2e56a959 | ||
|
|
143a5b53d0 | ||
|
|
fec088b7db | ||
|
|
d68609b407 | ||
|
|
8b1ae3f3ed | ||
|
|
0ccd671636 | ||
|
|
1dac3ad175 | ||
|
|
91e38a8361 | ||
|
|
071651aa67 | ||
|
|
a6c5ab4cd7 | ||
|
|
c28b2d4a73 | ||
|
|
8bca7692e2 | ||
|
|
d1b5b19dd6 | ||
|
|
d5ff8dae58 | ||
|
|
4bb5bb8af9 | ||
|
|
e03595d5bd | ||
|
|
b5cfe4ad2a | ||
|
|
4a64888977 | ||
|
|
44d082c163 | ||
|
|
8b3fc4cf5c | ||
|
|
5bd780f9c8 | ||
|
|
eba824b908 | ||
|
|
8de5f6d41c | ||
|
|
381be70991 | ||
|
|
87a6cac6b3 | ||
|
|
fef6c48fe1 | ||
|
|
3f59bd2ea4 | ||
|
|
e2d6cc3acb | ||
|
|
fef53fa72a | ||
|
|
6b2ad23059 | ||
|
|
368a059cfe | ||
|
|
67b4cbe08f | ||
|
|
0514f9f517 | ||
|
|
76bcc6c016 | ||
|
|
fd14c4c13e | ||
|
|
39dbdc129f | ||
|
|
dbaa38a5fe | ||
|
|
8f6e222ef2 | ||
|
|
88c5ff3dc7 | ||
|
|
4cf2cbc10c | ||
|
|
e4e3d8a55b | ||
|
|
d41fa4a006 | ||
|
|
48cdce6e65 | ||
|
|
2d8c099993 | ||
|
|
1c92604f3b | ||
|
|
6a63059b8c | ||
|
|
1de287bcdb | ||
|
|
f044fcc228 | ||
|
|
ee1712c245 | ||
|
|
8f03b89816 | ||
|
|
f751203867 | ||
|
|
4c4469b43b | ||
|
|
0e40dbfee3 | ||
|
|
d47db42925 | ||
|
|
04dcc8097a | ||
|
|
ec52966e5f | ||
|
|
b982958196 | ||
|
|
974872ed8e | ||
|
|
0edc9e3e8e | ||
|
|
7d4dd46e2c | ||
|
|
5169b74c86 | ||
|
|
b5dc195722 | ||
|
|
47a1d0c4b7 | ||
|
|
cacef37325 | ||
|
|
ab9aa60613 | ||
|
|
782b64a32f | ||
|
|
2f948c0616 | ||
|
|
2e3d9a03b6 | ||
|
|
0e48de379f | ||
|
|
08d2592311 | ||
|
|
313889e494 | ||
|
|
6058641b80 | ||
|
|
ba8ba61586 | ||
|
|
ec595e8bae | ||
|
|
743bf01b0d | ||
|
|
ca9cc4ad65 | ||
|
|
b2ee3aafa3 | ||
|
|
f137ea52db | ||
|
|
13888f806e | ||
|
|
778a3d1d6f | ||
|
|
8b277387a1 | ||
|
|
029a5f03fb | ||
|
|
2958843c5a | ||
|
|
daa95bdf02 | ||
|
|
316fffdd7c | ||
|
|
c65e2b2165 | ||
|
|
44b4842849 | ||
|
|
947c68f0a8 | ||
|
|
927c51a2bb | ||
|
|
07265e54ed | ||
|
|
00b00e6e77 | ||
|
|
1eabfebeb0 | ||
|
|
b9b6ee02c5 | ||
|
|
64f680bd70 | ||
|
|
e833835beb | ||
|
|
c358d28ca6 | ||
|
|
ba285f6ee8 | ||
|
|
72d7a158b2 | ||
|
|
c8f550a222 | ||
|
|
7324634620 | ||
|
|
b1d595be27 | ||
|
|
8e9def0e7e | ||
|
|
4141cefdec | ||
|
|
ffcdc70902 | ||
|
|
445173072d | ||
|
|
bb7b3c1a95 | ||
|
|
3d255abdaf | ||
|
|
308663a9fd | ||
|
|
b793060c05 | ||
|
|
008636f6fa | ||
|
|
467aaa1e07 | ||
|
|
8e79ba1efa | ||
|
|
d222085520 | ||
|
|
3d128c346e | ||
|
|
c4e387b408 | ||
|
|
788e477803 | ||
|
|
5983abbf68 | ||
|
|
2b096d0470 | ||
|
|
712bef33d8 | ||
|
|
7148869bac | ||
|
|
68ef61997d | ||
|
|
12ba1f793e | ||
|
|
e42b7e8c4c | ||
|
|
a8e491c22a | ||
|
|
570b5f4abf | ||
|
|
1c7ea0ab08 | ||
|
|
7bebf8ccfd | ||
|
|
29f039ff97 | ||
|
|
c45f41d171 | ||
|
|
a83e8c34c6 | ||
|
|
1e5e5e2e91 | ||
|
|
31025040dc | ||
|
|
ccc5fdb306 | ||
|
|
8bbf1388b1 | ||
|
|
33649d5d8d | ||
|
|
53bb36722c |
10
.github/workflows/ci.yml
vendored
10
.github/workflows/ci.yml
vendored
@@ -1,6 +1,12 @@
|
|||||||
name: CI
|
name: CI
|
||||||
|
|
||||||
on: [push, pull_request]
|
on:
|
||||||
|
push:
|
||||||
|
paths-ignore:
|
||||||
|
- '**/*.md'
|
||||||
|
pull_request:
|
||||||
|
paths-ignore:
|
||||||
|
- '**/*.md'
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
|
|
||||||
@@ -11,7 +17,7 @@ jobs:
|
|||||||
matrix:
|
matrix:
|
||||||
# The MODULE environment variable is evaluated in build-all.sh to run a subset
|
# The MODULE environment variable is evaluated in build-all.sh to run a subset
|
||||||
# of the builds. This way, multiple modules can be built in parallel.
|
# of the builds. This way, multiple modules can be built in parallel.
|
||||||
module: [ "module1", "module2", "module3", "module4", "module5" ]
|
module: [ "module1", "module2", "module3", "module4", "module5", "module6" ]
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
|
|
||||||
|
|||||||
2
.gitignore
vendored
2
.gitignore
vendored
@@ -1,3 +1,5 @@
|
|||||||
**/.idea/
|
**/.idea/
|
||||||
**/*.iml
|
**/*.iml
|
||||||
|
**/.DS_Store
|
||||||
|
**/.terraform
|
||||||
|
|
||||||
|
|||||||
BIN
aws/.DS_Store
vendored
BIN
aws/.DS_Store
vendored
Binary file not shown.
11
aws/aws-terraform/README.md
Normal file
11
aws/aws-terraform/README.md
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
# Terraform for creating AWS Resources
|
||||||
|
|
||||||
|
Example code to create/update AWS resources with Terraform.
|
||||||
|
Examples include Terraform capabilities of using modules, input variables and using Terraform cloud.
|
||||||
|
|
||||||
|
## Blog posts
|
||||||
|
|
||||||
|
Blog posts about this topic:
|
||||||
|
|
||||||
|
* [Using Terraform to create AWS resources](https://reflectoring.io/terraform-aws/)
|
||||||
|
|
||||||
41
aws/aws-terraform/aws-app-stack-cloud/main.tf
Normal file
41
aws/aws-terraform/aws-app-stack-cloud/main.tf
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
terraform {
|
||||||
|
|
||||||
|
backend "remote" {
|
||||||
|
hostname = "app.terraform.io"
|
||||||
|
organization = "pratikorg"
|
||||||
|
token = "pj7p59JFwSC4jQ.atlasv1.qfmTxLjTfaM5zKyaQrcGzuTojv6oCyLIoIAO7DkA2ieQY7OyINjINGGMiTczt62p1bs"
|
||||||
|
workspaces {
|
||||||
|
name = "my-tf-workspace"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
required_providers {
|
||||||
|
aws = {
|
||||||
|
source = "hashicorp/aws"
|
||||||
|
version = "~> 3.36"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
provider "aws" {
|
||||||
|
profile = "default"
|
||||||
|
region = "us-west-2"
|
||||||
|
}
|
||||||
|
|
||||||
|
module "app_server" {
|
||||||
|
source = "./modules/application"
|
||||||
|
|
||||||
|
ec2_instance_type = "t2.micro"
|
||||||
|
ami = "ami-830c94e3"
|
||||||
|
tags = {
|
||||||
|
Name = "server for web"
|
||||||
|
Env = "dev"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module "app_storage" {
|
||||||
|
source = "./modules/storage/"
|
||||||
|
|
||||||
|
bucket_name = "io.pratik.tf-example-bucket"
|
||||||
|
env = "dev"
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
resource "aws_instance" "vm-web" {
|
||||||
|
ami = var.ami
|
||||||
|
instance_type = var.ec2_instance_type
|
||||||
|
tags = var.tags
|
||||||
|
}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
output "instanceID" {
|
||||||
|
description = "ID of ec2 instance"
|
||||||
|
value = aws_instance.vm-web.id
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
variable "ec2_instance_type" {
|
||||||
|
description = "Instance type"
|
||||||
|
type = string
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "ami" {
|
||||||
|
description = "ami id"
|
||||||
|
type = string
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "tags" {
|
||||||
|
description = "Tags to set on the bucket."
|
||||||
|
type = map(string)
|
||||||
|
default = {Name = "server for web"
|
||||||
|
Env = "dev"}
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
resource "aws_s3_bucket" "s3_bucket" {
|
||||||
|
bucket = format("%s-%s",var.bucket_name,var.env)
|
||||||
|
acl = "private"
|
||||||
|
|
||||||
|
tags = {
|
||||||
|
Name = var.bucket_name
|
||||||
|
Environment = var.env
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
output "arn" {
|
||||||
|
description = "ARN of the bucket"
|
||||||
|
value = aws_s3_bucket.s3_bucket.arn
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
# Input variable definitions
|
||||||
|
|
||||||
|
variable "bucket_name" {
|
||||||
|
description = "Name of bucket"
|
||||||
|
type = string
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "env" {
|
||||||
|
description = "Environment like dev, prod"
|
||||||
|
type = string
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
24
aws/aws-terraform/aws-app-stack-input-vars/main.tf
Normal file
24
aws/aws-terraform/aws-app-stack-input-vars/main.tf
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
terraform {
|
||||||
|
required_providers {
|
||||||
|
aws = {
|
||||||
|
source = "hashicorp/aws"
|
||||||
|
version = "~> 3.27"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
provider "aws" {
|
||||||
|
profile = "default"
|
||||||
|
region = "us-west-2"
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "aws_instance" "vm-web" {
|
||||||
|
ami = "ami-830c94e3"
|
||||||
|
instance_type = var.ec2_instance_type
|
||||||
|
|
||||||
|
tags = {
|
||||||
|
Name = "server for web"
|
||||||
|
Env = "dev"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
ec2_instance_type = "t2.micro"
|
||||||
4
aws/aws-terraform/aws-app-stack-input-vars/variables.tf
Normal file
4
aws/aws-terraform/aws-app-stack-input-vars/variables.tf
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
variable "ec2_instance_type" {
|
||||||
|
description = "AWS EC2 instance type."
|
||||||
|
type = string
|
||||||
|
}
|
||||||
33
aws/aws-terraform/aws-app-stack-modules/main.tf
Normal file
33
aws/aws-terraform/aws-app-stack-modules/main.tf
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
terraform {
|
||||||
|
|
||||||
|
required_providers {
|
||||||
|
aws = {
|
||||||
|
source = "hashicorp/aws"
|
||||||
|
version = "~> 3.36"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
provider "aws" {
|
||||||
|
profile = "default"
|
||||||
|
region = "us-west-2"
|
||||||
|
}
|
||||||
|
|
||||||
|
module "app_server" {
|
||||||
|
source = "./modules/application"
|
||||||
|
|
||||||
|
ec2_instance_type = "t2.micro"
|
||||||
|
ami = "ami-830c94e3"
|
||||||
|
tags = {
|
||||||
|
Name = "server for web"
|
||||||
|
Env = "dev"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module "app_storage" {
|
||||||
|
source = "./modules/storage/"
|
||||||
|
|
||||||
|
bucket_name = "io.pratik.tf-example-bucket"
|
||||||
|
env = "dev"
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
resource "aws_instance" "vm-web" {
|
||||||
|
ami = var.ami
|
||||||
|
instance_type = var.ec2_instance_type
|
||||||
|
tags = var.tags
|
||||||
|
}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
output "instanceID" {
|
||||||
|
description = "ID of ec2 instance"
|
||||||
|
value = aws_instance.vm-web.id
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
variable "ec2_instance_type" {
|
||||||
|
description = "Instance type"
|
||||||
|
type = string
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "ami" {
|
||||||
|
description = "ami id"
|
||||||
|
type = string
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "tags" {
|
||||||
|
description = "Tags to set on the bucket."
|
||||||
|
type = map(string)
|
||||||
|
default = {Name = "server for web"
|
||||||
|
Env = "dev"}
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
resource "aws_s3_bucket" "s3_bucket" {
|
||||||
|
bucket = format("%s-%s",var.bucket_name,var.env)
|
||||||
|
acl = "private"
|
||||||
|
|
||||||
|
tags = {
|
||||||
|
Name = var.bucket_name
|
||||||
|
Environment = var.env
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
output "arn" {
|
||||||
|
description = "ARN of the bucket"
|
||||||
|
value = aws_s3_bucket.s3_bucket.arn
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
# Input variable definitions
|
||||||
|
|
||||||
|
variable "bucket_name" {
|
||||||
|
description = "Name of bucket"
|
||||||
|
type = string
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "env" {
|
||||||
|
description = "Environment like dev, prod"
|
||||||
|
type = string
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
24
aws/aws-terraform/aws-app-stack/main.tf
Normal file
24
aws/aws-terraform/aws-app-stack/main.tf
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
terraform {
|
||||||
|
required_providers {
|
||||||
|
aws = {
|
||||||
|
source = "hashicorp/aws"
|
||||||
|
version = "~> 3.27"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
provider "aws" {
|
||||||
|
profile = "default"
|
||||||
|
region = "us-west-2"
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "aws_instance" "vm-web" {
|
||||||
|
ami = "ami-830c94e3"
|
||||||
|
instance_type = "t2.micro"
|
||||||
|
|
||||||
|
tags = {
|
||||||
|
Name = "server for web"
|
||||||
|
Env = "dev"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
117
aws/cdk/.mvn/wrapper/MavenWrapperDownloader.java
vendored
Normal file
117
aws/cdk/.mvn/wrapper/MavenWrapperDownloader.java
vendored
Normal file
@@ -0,0 +1,117 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2007-present the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
import java.net.*;
|
||||||
|
import java.io.*;
|
||||||
|
import java.nio.channels.*;
|
||||||
|
import java.util.Properties;
|
||||||
|
|
||||||
|
public class MavenWrapperDownloader {
|
||||||
|
|
||||||
|
private static final String WRAPPER_VERSION = "0.5.6";
|
||||||
|
/**
|
||||||
|
* Default URL to download the maven-wrapper.jar from, if no 'downloadUrl' is provided.
|
||||||
|
*/
|
||||||
|
private static final String DEFAULT_DOWNLOAD_URL = "https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/"
|
||||||
|
+ WRAPPER_VERSION + "/maven-wrapper-" + WRAPPER_VERSION + ".jar";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Path to the maven-wrapper.properties file, which might contain a downloadUrl property to
|
||||||
|
* use instead of the default one.
|
||||||
|
*/
|
||||||
|
private static final String MAVEN_WRAPPER_PROPERTIES_PATH =
|
||||||
|
".mvn/wrapper/maven-wrapper.properties";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Path where the maven-wrapper.jar will be saved to.
|
||||||
|
*/
|
||||||
|
private static final String MAVEN_WRAPPER_JAR_PATH =
|
||||||
|
".mvn/wrapper/maven-wrapper.jar";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Name of the property which should be used to override the default download url for the wrapper.
|
||||||
|
*/
|
||||||
|
private static final String PROPERTY_NAME_WRAPPER_URL = "wrapperUrl";
|
||||||
|
|
||||||
|
public static void main(String args[]) {
|
||||||
|
System.out.println("- Downloader started");
|
||||||
|
File baseDirectory = new File(args[0]);
|
||||||
|
System.out.println("- Using base directory: " + baseDirectory.getAbsolutePath());
|
||||||
|
|
||||||
|
// If the maven-wrapper.properties exists, read it and check if it contains a custom
|
||||||
|
// wrapperUrl parameter.
|
||||||
|
File mavenWrapperPropertyFile = new File(baseDirectory, MAVEN_WRAPPER_PROPERTIES_PATH);
|
||||||
|
String url = DEFAULT_DOWNLOAD_URL;
|
||||||
|
if(mavenWrapperPropertyFile.exists()) {
|
||||||
|
FileInputStream mavenWrapperPropertyFileInputStream = null;
|
||||||
|
try {
|
||||||
|
mavenWrapperPropertyFileInputStream = new FileInputStream(mavenWrapperPropertyFile);
|
||||||
|
Properties mavenWrapperProperties = new Properties();
|
||||||
|
mavenWrapperProperties.load(mavenWrapperPropertyFileInputStream);
|
||||||
|
url = mavenWrapperProperties.getProperty(PROPERTY_NAME_WRAPPER_URL, url);
|
||||||
|
} catch (IOException e) {
|
||||||
|
System.out.println("- ERROR loading '" + MAVEN_WRAPPER_PROPERTIES_PATH + "'");
|
||||||
|
} finally {
|
||||||
|
try {
|
||||||
|
if(mavenWrapperPropertyFileInputStream != null) {
|
||||||
|
mavenWrapperPropertyFileInputStream.close();
|
||||||
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
|
// Ignore ...
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
System.out.println("- Downloading from: " + url);
|
||||||
|
|
||||||
|
File outputFile = new File(baseDirectory.getAbsolutePath(), MAVEN_WRAPPER_JAR_PATH);
|
||||||
|
if(!outputFile.getParentFile().exists()) {
|
||||||
|
if(!outputFile.getParentFile().mkdirs()) {
|
||||||
|
System.out.println(
|
||||||
|
"- ERROR creating output directory '" + outputFile.getParentFile().getAbsolutePath() + "'");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
System.out.println("- Downloading to: " + outputFile.getAbsolutePath());
|
||||||
|
try {
|
||||||
|
downloadFileFromURL(url, outputFile);
|
||||||
|
System.out.println("Done");
|
||||||
|
System.exit(0);
|
||||||
|
} catch (Throwable e) {
|
||||||
|
System.out.println("- Error downloading");
|
||||||
|
e.printStackTrace();
|
||||||
|
System.exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void downloadFileFromURL(String urlString, File destination) throws Exception {
|
||||||
|
if (System.getenv("MVNW_USERNAME") != null && System.getenv("MVNW_PASSWORD") != null) {
|
||||||
|
String username = System.getenv("MVNW_USERNAME");
|
||||||
|
char[] password = System.getenv("MVNW_PASSWORD").toCharArray();
|
||||||
|
Authenticator.setDefault(new Authenticator() {
|
||||||
|
@Override
|
||||||
|
protected PasswordAuthentication getPasswordAuthentication() {
|
||||||
|
return new PasswordAuthentication(username, password);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
URL website = new URL(urlString);
|
||||||
|
ReadableByteChannel rbc;
|
||||||
|
rbc = Channels.newChannel(website.openStream());
|
||||||
|
FileOutputStream fos = new FileOutputStream(destination);
|
||||||
|
fos.getChannel().transferFrom(rbc, 0, Long.MAX_VALUE);
|
||||||
|
fos.close();
|
||||||
|
rbc.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
BIN
aws/cdk/.mvn/wrapper/maven-wrapper.jar
vendored
Normal file
BIN
aws/cdk/.mvn/wrapper/maven-wrapper.jar
vendored
Normal file
Binary file not shown.
2
aws/cdk/.mvn/wrapper/maven-wrapper.properties
vendored
Normal file
2
aws/cdk/.mvn/wrapper/maven-wrapper.properties
vendored
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.6.3/apache-maven-3.6.3-bin.zip
|
||||||
|
wrapperUrl=https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar
|
||||||
0
aws/cdk/mvnw
vendored
Normal file
0
aws/cdk/mvnw
vendored
Normal file
182
aws/cdk/mvnw.cmd
vendored
Normal file
182
aws/cdk/mvnw.cmd
vendored
Normal file
@@ -0,0 +1,182 @@
|
|||||||
|
@REM ----------------------------------------------------------------------------
|
||||||
|
@REM Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
@REM or more contributor license agreements. See the NOTICE file
|
||||||
|
@REM distributed with this work for additional information
|
||||||
|
@REM regarding copyright ownership. The ASF licenses this file
|
||||||
|
@REM to you under the Apache License, Version 2.0 (the
|
||||||
|
@REM "License"); you may not use this file except in compliance
|
||||||
|
@REM with the License. You may obtain a copy of the License at
|
||||||
|
@REM
|
||||||
|
@REM http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
@REM
|
||||||
|
@REM Unless required by applicable law or agreed to in writing,
|
||||||
|
@REM software distributed under the License is distributed on an
|
||||||
|
@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
@REM KIND, either express or implied. See the License for the
|
||||||
|
@REM specific language governing permissions and limitations
|
||||||
|
@REM under the License.
|
||||||
|
@REM ----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
@REM ----------------------------------------------------------------------------
|
||||||
|
@REM Maven Start Up Batch script
|
||||||
|
@REM
|
||||||
|
@REM Required ENV vars:
|
||||||
|
@REM JAVA_HOME - location of a JDK home dir
|
||||||
|
@REM
|
||||||
|
@REM Optional ENV vars
|
||||||
|
@REM M2_HOME - location of maven2's installed home dir
|
||||||
|
@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands
|
||||||
|
@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending
|
||||||
|
@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven
|
||||||
|
@REM e.g. to debug Maven itself, use
|
||||||
|
@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
|
||||||
|
@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files
|
||||||
|
@REM ----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on'
|
||||||
|
@echo off
|
||||||
|
@REM set title of command window
|
||||||
|
title %0
|
||||||
|
@REM enable echoing by setting MAVEN_BATCH_ECHO to 'on'
|
||||||
|
@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO%
|
||||||
|
|
||||||
|
@REM set %HOME% to equivalent of $HOME
|
||||||
|
if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%")
|
||||||
|
|
||||||
|
@REM Execute a user defined script before this one
|
||||||
|
if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre
|
||||||
|
@REM check for pre script, once with legacy .bat ending and once with .cmd ending
|
||||||
|
if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat"
|
||||||
|
if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd"
|
||||||
|
:skipRcPre
|
||||||
|
|
||||||
|
@setlocal
|
||||||
|
|
||||||
|
set ERROR_CODE=0
|
||||||
|
|
||||||
|
@REM To isolate internal variables from possible post scripts, we use another setlocal
|
||||||
|
@setlocal
|
||||||
|
|
||||||
|
@REM ==== START VALIDATION ====
|
||||||
|
if not "%JAVA_HOME%" == "" goto OkJHome
|
||||||
|
|
||||||
|
echo.
|
||||||
|
echo Error: JAVA_HOME not found in your environment. >&2
|
||||||
|
echo Please set the JAVA_HOME variable in your environment to match the >&2
|
||||||
|
echo location of your Java installation. >&2
|
||||||
|
echo.
|
||||||
|
goto error
|
||||||
|
|
||||||
|
:OkJHome
|
||||||
|
if exist "%JAVA_HOME%\bin\java.exe" goto init
|
||||||
|
|
||||||
|
echo.
|
||||||
|
echo Error: JAVA_HOME is set to an invalid directory. >&2
|
||||||
|
echo JAVA_HOME = "%JAVA_HOME%" >&2
|
||||||
|
echo Please set the JAVA_HOME variable in your environment to match the >&2
|
||||||
|
echo location of your Java installation. >&2
|
||||||
|
echo.
|
||||||
|
goto error
|
||||||
|
|
||||||
|
@REM ==== END VALIDATION ====
|
||||||
|
|
||||||
|
:init
|
||||||
|
|
||||||
|
@REM Find the project base dir, i.e. the directory that contains the folder ".mvn".
|
||||||
|
@REM Fallback to current working directory if not found.
|
||||||
|
|
||||||
|
set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR%
|
||||||
|
IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir
|
||||||
|
|
||||||
|
set EXEC_DIR=%CD%
|
||||||
|
set WDIR=%EXEC_DIR%
|
||||||
|
:findBaseDir
|
||||||
|
IF EXIST "%WDIR%"\.mvn goto baseDirFound
|
||||||
|
cd ..
|
||||||
|
IF "%WDIR%"=="%CD%" goto baseDirNotFound
|
||||||
|
set WDIR=%CD%
|
||||||
|
goto findBaseDir
|
||||||
|
|
||||||
|
:baseDirFound
|
||||||
|
set MAVEN_PROJECTBASEDIR=%WDIR%
|
||||||
|
cd "%EXEC_DIR%"
|
||||||
|
goto endDetectBaseDir
|
||||||
|
|
||||||
|
:baseDirNotFound
|
||||||
|
set MAVEN_PROJECTBASEDIR=%EXEC_DIR%
|
||||||
|
cd "%EXEC_DIR%"
|
||||||
|
|
||||||
|
:endDetectBaseDir
|
||||||
|
|
||||||
|
IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig
|
||||||
|
|
||||||
|
@setlocal EnableExtensions EnableDelayedExpansion
|
||||||
|
for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a
|
||||||
|
@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS%
|
||||||
|
|
||||||
|
:endReadAdditionalConfig
|
||||||
|
|
||||||
|
SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe"
|
||||||
|
set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar"
|
||||||
|
set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
|
||||||
|
|
||||||
|
set DOWNLOAD_URL="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar"
|
||||||
|
|
||||||
|
FOR /F "tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO (
|
||||||
|
IF "%%A"=="wrapperUrl" SET DOWNLOAD_URL=%%B
|
||||||
|
)
|
||||||
|
|
||||||
|
@REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central
|
||||||
|
@REM This allows using the maven wrapper in projects that prohibit checking in binary data.
|
||||||
|
if exist %WRAPPER_JAR% (
|
||||||
|
if "%MVNW_VERBOSE%" == "true" (
|
||||||
|
echo Found %WRAPPER_JAR%
|
||||||
|
)
|
||||||
|
) else (
|
||||||
|
if not "%MVNW_REPOURL%" == "" (
|
||||||
|
SET DOWNLOAD_URL="%MVNW_REPOURL%/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar"
|
||||||
|
)
|
||||||
|
if "%MVNW_VERBOSE%" == "true" (
|
||||||
|
echo Couldn't find %WRAPPER_JAR%, downloading it ...
|
||||||
|
echo Downloading from: %DOWNLOAD_URL%
|
||||||
|
)
|
||||||
|
|
||||||
|
powershell -Command "&{"^
|
||||||
|
"$webclient = new-object System.Net.WebClient;"^
|
||||||
|
"if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^
|
||||||
|
"$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^
|
||||||
|
"}"^
|
||||||
|
"[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%DOWNLOAD_URL%', '%WRAPPER_JAR%')"^
|
||||||
|
"}"
|
||||||
|
if "%MVNW_VERBOSE%" == "true" (
|
||||||
|
echo Finished downloading %WRAPPER_JAR%
|
||||||
|
)
|
||||||
|
)
|
||||||
|
@REM End of extension
|
||||||
|
|
||||||
|
@REM Provide a "standardized" way to retrieve the CLI args that will
|
||||||
|
@REM work with both Windows and non-Windows executions.
|
||||||
|
set MAVEN_CMD_LINE_ARGS=%*
|
||||||
|
|
||||||
|
%MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %*
|
||||||
|
if ERRORLEVEL 1 goto error
|
||||||
|
goto end
|
||||||
|
|
||||||
|
:error
|
||||||
|
set ERROR_CODE=1
|
||||||
|
|
||||||
|
:end
|
||||||
|
@endlocal & set ERROR_CODE=%ERROR_CODE%
|
||||||
|
|
||||||
|
if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost
|
||||||
|
@REM check for post script, once with legacy .bat ending and once with .cmd ending
|
||||||
|
if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat"
|
||||||
|
if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd"
|
||||||
|
:skipRcPost
|
||||||
|
|
||||||
|
@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on'
|
||||||
|
if "%MAVEN_BATCH_PAUSE%" == "on" pause
|
||||||
|
|
||||||
|
if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE%
|
||||||
|
|
||||||
|
exit /B %ERROR_CODE%
|
||||||
23
aws/cdk/src/main/java/com/myorg/SecondStack.java
Normal file
23
aws/cdk/src/main/java/com/myorg/SecondStack.java
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
package com.myorg;
|
||||||
|
|
||||||
|
import software.amazon.awscdk.core.Construct;
|
||||||
|
import software.amazon.awscdk.core.Stack;
|
||||||
|
import software.amazon.awscdk.core.StackProps;
|
||||||
|
import software.amazon.awscdk.services.s3.Bucket;
|
||||||
|
|
||||||
|
public class SecondStack extends Stack {
|
||||||
|
public SecondStack(final Construct scope, final String id) {
|
||||||
|
this(scope, id, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public SecondStack(final Construct scope, final String id, final StackProps props) {
|
||||||
|
super(scope, id, props);
|
||||||
|
|
||||||
|
String bucketName = (String) this.getNode().tryGetContext("bucketName2");
|
||||||
|
|
||||||
|
Bucket bucket = Bucket.Builder.create(this, "bucket")
|
||||||
|
.bucketName(bucketName)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -65,6 +65,11 @@
|
|||||||
</exclusion>
|
</exclusion>
|
||||||
</exclusions>
|
</exclusions>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.junit.jupiter</groupId>
|
||||||
|
<artifactId>junit-jupiter-engine</artifactId>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
<build>
|
<build>
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import io.reflectoring.customerregistration.dtos.CustomerCreateRequest;
|
|||||||
import io.reflectoring.customerregistration.repositories.CustomerImageStore;
|
import io.reflectoring.customerregistration.repositories.CustomerImageStore;
|
||||||
import org.junit.jupiter.api.AfterEach;
|
import org.junit.jupiter.api.AfterEach;
|
||||||
import org.junit.jupiter.api.BeforeEach;
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
|
import org.junit.jupiter.api.Disabled;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import org.junit.jupiter.api.extension.ExtendWith;
|
import org.junit.jupiter.api.extension.ExtendWith;
|
||||||
|
|
||||||
@@ -34,6 +35,7 @@ import software.amazon.awssdk.services.s3.model.NoSuchKeyException;
|
|||||||
@ExtendWith(LocalstackDockerExtension.class)
|
@ExtendWith(LocalstackDockerExtension.class)
|
||||||
@ActiveProfiles("local")
|
@ActiveProfiles("local")
|
||||||
@LocalstackDockerProperties(services = { "s3" })
|
@LocalstackDockerProperties(services = { "s3" })
|
||||||
|
@Disabled(value = "Disabled because starting Docker containers like this does not run on GitHub Actions.")
|
||||||
class CustomerImageStoreTest {
|
class CustomerImageStoreTest {
|
||||||
|
|
||||||
private static final Region region = Region.US_EAST_1;
|
private static final Region region = Region.US_EAST_1;
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import io.reflectoring.customerregistration.dtos.CustomerDto;
|
|||||||
import io.reflectoring.customerregistration.repositories.CustomerProfileStore;
|
import io.reflectoring.customerregistration.repositories.CustomerProfileStore;
|
||||||
import org.junit.jupiter.api.AfterEach;
|
import org.junit.jupiter.api.AfterEach;
|
||||||
import org.junit.jupiter.api.BeforeEach;
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
|
import org.junit.jupiter.api.Disabled;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import org.junit.jupiter.api.extension.ExtendWith;
|
import org.junit.jupiter.api.extension.ExtendWith;
|
||||||
|
|
||||||
@@ -37,6 +38,7 @@ import software.amazon.awssdk.services.dynamodb.model.ScalarAttributeType;
|
|||||||
@ExtendWith(LocalstackDockerExtension.class)
|
@ExtendWith(LocalstackDockerExtension.class)
|
||||||
@ActiveProfiles("local")
|
@ActiveProfiles("local")
|
||||||
@LocalstackDockerProperties(services = { "dynamodb"})
|
@LocalstackDockerProperties(services = { "dynamodb"})
|
||||||
|
@Disabled(value = "Disabled because starting Docker containers like this does not run on GitHub Actions.")
|
||||||
class CustomerProfileStoreTest {
|
class CustomerProfileStoreTest {
|
||||||
|
|
||||||
private static final Region region = Region.US_EAST_1;
|
private static final Region region = Region.US_EAST_1;
|
||||||
|
|||||||
141
aws/s3/.gitignore
vendored
Normal file
141
aws/s3/.gitignore
vendored
Normal file
@@ -0,0 +1,141 @@
|
|||||||
|
|
||||||
|
# Created by https://www.toptal.com/developers/gitignore/api/java,intellij+all,gradle
|
||||||
|
# Edit at https://www.toptal.com/developers/gitignore?templates=java,intellij+all,gradle
|
||||||
|
|
||||||
|
### Intellij+all ###
|
||||||
|
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider
|
||||||
|
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
|
||||||
|
|
||||||
|
# User-specific stuff
|
||||||
|
.idea/**/workspace.xml
|
||||||
|
.idea/**/tasks.xml
|
||||||
|
.idea/**/usage.statistics.xml
|
||||||
|
.idea/**/dictionaries
|
||||||
|
.idea/**/shelf
|
||||||
|
|
||||||
|
# Generated files
|
||||||
|
.idea/**/contentModel.xml
|
||||||
|
|
||||||
|
# Sensitive or high-churn files
|
||||||
|
.idea/**/dataSources/
|
||||||
|
.idea/**/dataSources.ids
|
||||||
|
.idea/**/dataSources.local.xml
|
||||||
|
.idea/**/sqlDataSources.xml
|
||||||
|
.idea/**/dynamic.xml
|
||||||
|
.idea/**/uiDesigner.xml
|
||||||
|
.idea/**/dbnavigator.xml
|
||||||
|
|
||||||
|
# Gradle
|
||||||
|
.idea/**/gradle.xml
|
||||||
|
.idea/**/libraries
|
||||||
|
|
||||||
|
# Gradle and Maven with auto-import
|
||||||
|
# When using Gradle or Maven with auto-import, you should exclude module files,
|
||||||
|
# since they will be recreated, and may cause churn. Uncomment if using
|
||||||
|
# auto-import.
|
||||||
|
# .idea/artifacts
|
||||||
|
# .idea/compiler.xml
|
||||||
|
# .idea/jarRepositories.xml
|
||||||
|
# .idea/modules.xml
|
||||||
|
# .idea/*.iml
|
||||||
|
# .idea/modules
|
||||||
|
# *.iml
|
||||||
|
# *.ipr
|
||||||
|
|
||||||
|
# CMake
|
||||||
|
cmake-build-*/
|
||||||
|
|
||||||
|
# Mongo Explorer plugin
|
||||||
|
.idea/**/mongoSettings.xml
|
||||||
|
|
||||||
|
# File-based project format
|
||||||
|
*.iws
|
||||||
|
|
||||||
|
# IntelliJ
|
||||||
|
# out/
|
||||||
|
|
||||||
|
# mpeltonen/sbt-idea plugin
|
||||||
|
.idea_modules/
|
||||||
|
|
||||||
|
# JIRA plugin
|
||||||
|
atlassian-ide-plugin.xml
|
||||||
|
|
||||||
|
# Cursive Clojure plugin
|
||||||
|
.idea/replstate.xml
|
||||||
|
|
||||||
|
# Crashlytics plugin (for Android Studio and IntelliJ)
|
||||||
|
com_crashlytics_export_strings.xml
|
||||||
|
crashlytics.properties
|
||||||
|
crashlytics-build.properties
|
||||||
|
fabric.properties
|
||||||
|
|
||||||
|
# Editor-based Rest Client
|
||||||
|
.idea/httpRequests
|
||||||
|
|
||||||
|
# Android studio 3.1+ serialized cache file
|
||||||
|
.idea/caches/build_file_checksums.ser
|
||||||
|
|
||||||
|
### Intellij+all Patch ###
|
||||||
|
# Ignores the whole .idea folder and all .iml files
|
||||||
|
# See https://github.com/joeblau/gitignore.io/issues/186 and https://github.com/joeblau/gitignore.io/issues/360
|
||||||
|
|
||||||
|
.idea/
|
||||||
|
|
||||||
|
# Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-249601023
|
||||||
|
|
||||||
|
*.iml
|
||||||
|
modules.xml
|
||||||
|
.idea/misc.xml
|
||||||
|
*.ipr
|
||||||
|
|
||||||
|
# Sonarlint plugin
|
||||||
|
.idea/sonarlint
|
||||||
|
|
||||||
|
### Java ###
|
||||||
|
# Compiled class file
|
||||||
|
*.class
|
||||||
|
|
||||||
|
# Log file
|
||||||
|
*.log
|
||||||
|
|
||||||
|
# BlueJ files
|
||||||
|
*.ctxt
|
||||||
|
|
||||||
|
# Mobile Tools for Java (J2ME)
|
||||||
|
.mtj.tmp/
|
||||||
|
|
||||||
|
# Package Files #
|
||||||
|
*.jar
|
||||||
|
*.war
|
||||||
|
*.nar
|
||||||
|
*.ear
|
||||||
|
*.zip
|
||||||
|
*.tar.gz
|
||||||
|
*.rar
|
||||||
|
|
||||||
|
# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
|
||||||
|
hs_err_pid*
|
||||||
|
|
||||||
|
### Gradle ###
|
||||||
|
.gradle
|
||||||
|
build/
|
||||||
|
|
||||||
|
# Ignore Gradle GUI config
|
||||||
|
gradle-app.setting
|
||||||
|
|
||||||
|
# Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored)
|
||||||
|
!gradle-wrapper.jar
|
||||||
|
|
||||||
|
# Cache of project
|
||||||
|
.gradletasknamecache
|
||||||
|
|
||||||
|
# # Work around https://youtrack.jetbrains.com/issue/IDEA-116898
|
||||||
|
# gradle/wrapper/gradle-wrapper.properties
|
||||||
|
|
||||||
|
### Gradle Patch ###
|
||||||
|
**/build/
|
||||||
|
|
||||||
|
# End of https://www.toptal.com/developers/gitignore/api/java,intellij+all,gradle
|
||||||
|
|
||||||
|
# Dumps of the meta-db
|
||||||
|
data/*.db
|
||||||
167
aws/s3/Readme.md
Normal file
167
aws/s3/Readme.md
Normal file
@@ -0,0 +1,167 @@
|
|||||||
|
<!-- PROJECT LOGO -->
|
||||||
|
<br />
|
||||||
|
<p align="center">
|
||||||
|
<img src="images/logo.png" alt="Logo" width="200" height="200">
|
||||||
|
<h3 align="center">Private File Upload</h3>
|
||||||
|
|
||||||
|
<p align="center">
|
||||||
|
Build on Spring Cloud & AWS S3
|
||||||
|
</p>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<!-- TABLE OF CONTENTS -->
|
||||||
|
## Table of Contents
|
||||||
|
|
||||||
|
* [About the Project](#about-the-project)
|
||||||
|
* [Built With](#built-with)
|
||||||
|
* [Getting Started](#getting-started)
|
||||||
|
* [Prerequisites](#prerequisites)
|
||||||
|
* [Installation](#installation)
|
||||||
|
* [Usage](#usage)
|
||||||
|
* [Contact](#contact)
|
||||||
|
* [Acknowledgements](#acknowledgements)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<!-- ABOUT THE PROJECT -->
|
||||||
|
## About The Project
|
||||||
|
|
||||||
|
<p align="center">
|
||||||
|
<img src="images/usage.gif">
|
||||||
|
</p>
|
||||||
|
|
||||||
|
|
||||||
|
### Built With
|
||||||
|
- Spring Boot
|
||||||
|
- Spring Cloud
|
||||||
|
- AWS S3
|
||||||
|
- ☕️ & ❤️
|
||||||
|
|
||||||
|
<!-- GETTING STARTED -->
|
||||||
|
## Getting Started
|
||||||
|
|
||||||
|
### Prerequisites
|
||||||
|
|
||||||
|
In order to run this application you should have an Amazon Web Services (AWS) account.
|
||||||
|
|
||||||
|
### Installation
|
||||||
|
|
||||||
|
1. Update the `application.yaml` settings value for `cloud.aws.credentials.profile-name` to the name of your local AWS-profile
|
||||||
|
2. run `./gradlew bootRun`
|
||||||
|
3. Open your browser at `localhost:8080`
|
||||||
|
|
||||||
|
<!-- USAGE EXAMPLES -->
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
The application wraps a lot of S3-related API calls in easy to use UI elements to
|
||||||
|
|
||||||
|
- create/delete buckets (mapped as "spaces" so we can give them non-unique names)
|
||||||
|
- upload/delete files
|
||||||
|
- make files public/private
|
||||||
|
- create pre-signed URLs for files
|
||||||
|
- create bucket policy rules to set the lifecycle duration for elements
|
||||||
|
|
||||||
|
More detailed instructions can be found here:
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>🪣 Create a bucket</summary>
|
||||||
|
|
||||||
|
1. Navigate to the _Spaces_ section
|
||||||
|
2. Click on _New Space_
|
||||||
|
3. Enter the name and click _Submit_
|
||||||
|
4. A message should pop up to indicate success
|
||||||
|
|
||||||
|
</details>
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>🗂 Upload a File</summary>
|
||||||
|
|
||||||
|
1. Navigate to the _Spaces_ section
|
||||||
|
2. Select _Details_ on the target Space/Bucket
|
||||||
|
3. Click on _Upload File_
|
||||||
|
4. Pick our file, provide a name and click _Submit_
|
||||||
|
5. A message should pop up to indicate success
|
||||||
|
</details>
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>🔎 List all Objects in a Bucket</summary>
|
||||||
|
|
||||||
|
1. Navigate to the _Spaces_ section
|
||||||
|
2. Select _Details_ on the target Space/Bucket
|
||||||
|
3. You see a list of all objects stored in the bucket
|
||||||
|
</details>
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>🌐 Get an Object's URL</summary>
|
||||||
|
|
||||||
|
1. Navigate to the _Spaces_ section
|
||||||
|
2. Select _Details_ on the target Space/Bucket
|
||||||
|
3. Select _Download_ on the target object
|
||||||
|
4. The object's URL shall be opened in a new tab
|
||||||
|
</details>
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>📢 Make an object public</summary>
|
||||||
|
|
||||||
|
1. Navigate to the _Spaces_ section
|
||||||
|
2. Select _Details_ on the target Space/Bucket
|
||||||
|
3. Select _Make Public_ on the target object
|
||||||
|
4. A message should pop up to indicate success
|
||||||
|
</details>
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>🤫 Make an Object private</summary>
|
||||||
|
|
||||||
|
1. Navigate to the _Spaces_ section
|
||||||
|
2. Select _Details_ on the target Space/Bucket
|
||||||
|
3. Select _Make Private_ on the target object
|
||||||
|
4. A message should pop up to indicate success
|
||||||
|
</details>
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>🔥 Delete an Object</summary>
|
||||||
|
|
||||||
|
1. Navigate to the _Spaces_ section
|
||||||
|
2. Select _Details_ on the target Space/Bucket
|
||||||
|
3. Select _Delete_ on the target object
|
||||||
|
4. The list of objects should reload without the deleted one
|
||||||
|
</details>
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>☄️ Delete a Bucket</summary>
|
||||||
|
|
||||||
|
1. Navigate to the _Spaces_ section
|
||||||
|
2. Select _Delete_ on the target Space/Bucket
|
||||||
|
3. The list of buckets should reload without the deleted one
|
||||||
|
</details>
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>👾 Generate a pre-signed URL</summary>
|
||||||
|
|
||||||
|
1. Navigate to the _Spaces_ section
|
||||||
|
2. Select _Details_ on the target Space/Bucket
|
||||||
|
3. Select _Magic Link_ on the target object
|
||||||
|
4. A message should pop up, containing a pre-signed URL for that object (which is valid for 15 minutes)
|
||||||
|
</details>
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>⏳ Set Expiration on Bucket</summary>
|
||||||
|
|
||||||
|
1. Navigate to the _Spaces_ section
|
||||||
|
2. Select _Make Temporary_ on the target Space/Bucket
|
||||||
|
3. Select _Delete_ on the target object
|
||||||
|
4. A message should pop up to indicate success
|
||||||
|
</details>
|
||||||
|
|
||||||
|
<!-- CONTACT -->
|
||||||
|
## Contact
|
||||||
|
|
||||||
|
Joshua Görner - [jgoerner](https://www.linkedin.com/in/jgoerner/) - joshua.goerner[at]gmail.com
|
||||||
|
|
||||||
|
|
||||||
|
<!-- ACKNOWLEDGEMENTS -->
|
||||||
|
## Acknowledgements
|
||||||
|
* [O. Drew](https://github.com/othneildrew/Best-README-Template) - nice GH Readme template
|
||||||
|
* [Hatchful](https://hatchful.shopify.com/) - Easy Logo Generation
|
||||||
41
aws/s3/build.gradle
Normal file
41
aws/s3/build.gradle
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
plugins {
|
||||||
|
id 'org.springframework.boot' version '2.4.2'
|
||||||
|
id 'io.spring.dependency-management' version '1.0.11.RELEASE'
|
||||||
|
id 'java'
|
||||||
|
}
|
||||||
|
|
||||||
|
group = 'io.jgoerner'
|
||||||
|
version = '0.0.1-SNAPSHOT'
|
||||||
|
sourceCompatibility = '11'
|
||||||
|
|
||||||
|
repositories {
|
||||||
|
mavenCentral()
|
||||||
|
}
|
||||||
|
|
||||||
|
ext {
|
||||||
|
// set('springCloudVersion', "Hoxton.SR9")
|
||||||
|
set('springCloudVersion', "Finchley.SR1")
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
annotationProcessor 'org.projectlombok:lombok'
|
||||||
|
developmentOnly 'org.springframework.boot:spring-boot-devtools'
|
||||||
|
implementation 'org.springframework.boot:spring-boot-starter-web'
|
||||||
|
implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
|
||||||
|
implementation 'org.springframework.cloud:spring-cloud-starter-aws'
|
||||||
|
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
|
||||||
|
implementation 'org.projectlombok:lombok:1.18.16'
|
||||||
|
runtimeOnly 'com.h2database:h2'
|
||||||
|
testImplementation 'org.springframework.boot:spring-boot-starter-test'
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencyManagement {
|
||||||
|
imports {
|
||||||
|
mavenBom "org.springframework.cloud:spring-cloud-dependencies:${springCloudVersion}"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
test {
|
||||||
|
useJUnitPlatform()
|
||||||
|
}
|
||||||
0
aws/s3/data/.gitkeep
Normal file
0
aws/s3/data/.gitkeep
Normal file
BIN
aws/s3/gradle/wrapper/gradle-wrapper.jar
vendored
Normal file
BIN
aws/s3/gradle/wrapper/gradle-wrapper.jar
vendored
Normal file
Binary file not shown.
6
aws/s3/gradle/wrapper/gradle-wrapper.properties
vendored
Normal file
6
aws/s3/gradle/wrapper/gradle-wrapper.properties
vendored
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
#Mon Jan 04 22:38:53 CET 2021
|
||||||
|
distributionUrl=https\://services.gradle.org/distributions/gradle-6.7.1-all.zip
|
||||||
|
distributionBase=GRADLE_USER_HOME
|
||||||
|
distributionPath=wrapper/dists
|
||||||
|
zipStorePath=wrapper/dists
|
||||||
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
185
aws/s3/gradlew
vendored
Executable file
185
aws/s3/gradlew
vendored
Executable file
@@ -0,0 +1,185 @@
|
|||||||
|
#!/usr/bin/env sh
|
||||||
|
|
||||||
|
#
|
||||||
|
# Copyright 2015 the original author or authors.
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
# you may not use this file except in compliance with the License.
|
||||||
|
# You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
#
|
||||||
|
|
||||||
|
##############################################################################
|
||||||
|
##
|
||||||
|
## Gradle start up script for UN*X
|
||||||
|
##
|
||||||
|
##############################################################################
|
||||||
|
|
||||||
|
# Attempt to set APP_HOME
|
||||||
|
# Resolve links: $0 may be a link
|
||||||
|
PRG="$0"
|
||||||
|
# Need this for relative symlinks.
|
||||||
|
while [ -h "$PRG" ] ; do
|
||||||
|
ls=`ls -ld "$PRG"`
|
||||||
|
link=`expr "$ls" : '.*-> \(.*\)$'`
|
||||||
|
if expr "$link" : '/.*' > /dev/null; then
|
||||||
|
PRG="$link"
|
||||||
|
else
|
||||||
|
PRG=`dirname "$PRG"`"/$link"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
SAVED="`pwd`"
|
||||||
|
cd "`dirname \"$PRG\"`/" >/dev/null
|
||||||
|
APP_HOME="`pwd -P`"
|
||||||
|
cd "$SAVED" >/dev/null
|
||||||
|
|
||||||
|
APP_NAME="Gradle"
|
||||||
|
APP_BASE_NAME=`basename "$0"`
|
||||||
|
|
||||||
|
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||||
|
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
||||||
|
|
||||||
|
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||||
|
MAX_FD="maximum"
|
||||||
|
|
||||||
|
warn () {
|
||||||
|
echo "$*"
|
||||||
|
}
|
||||||
|
|
||||||
|
die () {
|
||||||
|
echo
|
||||||
|
echo "$*"
|
||||||
|
echo
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# OS specific support (must be 'true' or 'false').
|
||||||
|
cygwin=false
|
||||||
|
msys=false
|
||||||
|
darwin=false
|
||||||
|
nonstop=false
|
||||||
|
case "`uname`" in
|
||||||
|
CYGWIN* )
|
||||||
|
cygwin=true
|
||||||
|
;;
|
||||||
|
Darwin* )
|
||||||
|
darwin=true
|
||||||
|
;;
|
||||||
|
MINGW* )
|
||||||
|
msys=true
|
||||||
|
;;
|
||||||
|
NONSTOP* )
|
||||||
|
nonstop=true
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
|
||||||
|
|
||||||
|
|
||||||
|
# Determine the Java command to use to start the JVM.
|
||||||
|
if [ -n "$JAVA_HOME" ] ; then
|
||||||
|
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
|
||||||
|
# IBM's JDK on AIX uses strange locations for the executables
|
||||||
|
JAVACMD="$JAVA_HOME/jre/sh/java"
|
||||||
|
else
|
||||||
|
JAVACMD="$JAVA_HOME/bin/java"
|
||||||
|
fi
|
||||||
|
if [ ! -x "$JAVACMD" ] ; then
|
||||||
|
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
|
||||||
|
|
||||||
|
Please set the JAVA_HOME variable in your environment to match the
|
||||||
|
location of your Java installation."
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
JAVACMD="java"
|
||||||
|
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||||
|
|
||||||
|
Please set the JAVA_HOME variable in your environment to match the
|
||||||
|
location of your Java installation."
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Increase the maximum file descriptors if we can.
|
||||||
|
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
|
||||||
|
MAX_FD_LIMIT=`ulimit -H -n`
|
||||||
|
if [ $? -eq 0 ] ; then
|
||||||
|
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
|
||||||
|
MAX_FD="$MAX_FD_LIMIT"
|
||||||
|
fi
|
||||||
|
ulimit -n $MAX_FD
|
||||||
|
if [ $? -ne 0 ] ; then
|
||||||
|
warn "Could not set maximum file descriptor limit: $MAX_FD"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# For Darwin, add options to specify how the application appears in the dock
|
||||||
|
if $darwin; then
|
||||||
|
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
|
||||||
|
fi
|
||||||
|
|
||||||
|
# For Cygwin or MSYS, switch paths to Windows format before running java
|
||||||
|
if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
|
||||||
|
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
|
||||||
|
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
|
||||||
|
|
||||||
|
JAVACMD=`cygpath --unix "$JAVACMD"`
|
||||||
|
|
||||||
|
# We build the pattern for arguments to be converted via cygpath
|
||||||
|
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
|
||||||
|
SEP=""
|
||||||
|
for dir in $ROOTDIRSRAW ; do
|
||||||
|
ROOTDIRS="$ROOTDIRS$SEP$dir"
|
||||||
|
SEP="|"
|
||||||
|
done
|
||||||
|
OURCYGPATTERN="(^($ROOTDIRS))"
|
||||||
|
# Add a user-defined pattern to the cygpath arguments
|
||||||
|
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
|
||||||
|
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
|
||||||
|
fi
|
||||||
|
# Now convert the arguments - kludge to limit ourselves to /bin/sh
|
||||||
|
i=0
|
||||||
|
for arg in "$@" ; do
|
||||||
|
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
|
||||||
|
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
|
||||||
|
|
||||||
|
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
|
||||||
|
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
|
||||||
|
else
|
||||||
|
eval `echo args$i`="\"$arg\""
|
||||||
|
fi
|
||||||
|
i=`expr $i + 1`
|
||||||
|
done
|
||||||
|
case $i in
|
||||||
|
0) set -- ;;
|
||||||
|
1) set -- "$args0" ;;
|
||||||
|
2) set -- "$args0" "$args1" ;;
|
||||||
|
3) set -- "$args0" "$args1" "$args2" ;;
|
||||||
|
4) set -- "$args0" "$args1" "$args2" "$args3" ;;
|
||||||
|
5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
|
||||||
|
6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
|
||||||
|
7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
|
||||||
|
8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
|
||||||
|
9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
|
||||||
|
esac
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Escape application args
|
||||||
|
save () {
|
||||||
|
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
|
||||||
|
echo " "
|
||||||
|
}
|
||||||
|
APP_ARGS=`save "$@"`
|
||||||
|
|
||||||
|
# Collect all arguments for the java command, following the shell quoting and substitution rules
|
||||||
|
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
|
||||||
|
|
||||||
|
exec "$JAVACMD" "$@"
|
||||||
89
aws/s3/gradlew.bat
vendored
Normal file
89
aws/s3/gradlew.bat
vendored
Normal file
@@ -0,0 +1,89 @@
|
|||||||
|
@rem
|
||||||
|
@rem Copyright 2015 the original author or authors.
|
||||||
|
@rem
|
||||||
|
@rem Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
@rem you may not use this file except in compliance with the License.
|
||||||
|
@rem You may obtain a copy of the License at
|
||||||
|
@rem
|
||||||
|
@rem https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
@rem
|
||||||
|
@rem Unless required by applicable law or agreed to in writing, software
|
||||||
|
@rem distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
@rem See the License for the specific language governing permissions and
|
||||||
|
@rem limitations under the License.
|
||||||
|
@rem
|
||||||
|
|
||||||
|
@if "%DEBUG%" == "" @echo off
|
||||||
|
@rem ##########################################################################
|
||||||
|
@rem
|
||||||
|
@rem Gradle startup script for Windows
|
||||||
|
@rem
|
||||||
|
@rem ##########################################################################
|
||||||
|
|
||||||
|
@rem Set local scope for the variables with windows NT shell
|
||||||
|
if "%OS%"=="Windows_NT" setlocal
|
||||||
|
|
||||||
|
set DIRNAME=%~dp0
|
||||||
|
if "%DIRNAME%" == "" set DIRNAME=.
|
||||||
|
set APP_BASE_NAME=%~n0
|
||||||
|
set APP_HOME=%DIRNAME%
|
||||||
|
|
||||||
|
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
|
||||||
|
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
|
||||||
|
|
||||||
|
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||||
|
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
|
||||||
|
|
||||||
|
@rem Find java.exe
|
||||||
|
if defined JAVA_HOME goto findJavaFromJavaHome
|
||||||
|
|
||||||
|
set JAVA_EXE=java.exe
|
||||||
|
%JAVA_EXE% -version >NUL 2>&1
|
||||||
|
if "%ERRORLEVEL%" == "0" goto execute
|
||||||
|
|
||||||
|
echo.
|
||||||
|
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||||
|
echo.
|
||||||
|
echo Please set the JAVA_HOME variable in your environment to match the
|
||||||
|
echo location of your Java installation.
|
||||||
|
|
||||||
|
goto fail
|
||||||
|
|
||||||
|
:findJavaFromJavaHome
|
||||||
|
set JAVA_HOME=%JAVA_HOME:"=%
|
||||||
|
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
||||||
|
|
||||||
|
if exist "%JAVA_EXE%" goto execute
|
||||||
|
|
||||||
|
echo.
|
||||||
|
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
|
||||||
|
echo.
|
||||||
|
echo Please set the JAVA_HOME variable in your environment to match the
|
||||||
|
echo location of your Java installation.
|
||||||
|
|
||||||
|
goto fail
|
||||||
|
|
||||||
|
:execute
|
||||||
|
@rem Setup the command line
|
||||||
|
|
||||||
|
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
|
||||||
|
|
||||||
|
|
||||||
|
@rem Execute Gradle
|
||||||
|
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
|
||||||
|
|
||||||
|
:end
|
||||||
|
@rem End local scope for the variables with windows NT shell
|
||||||
|
if "%ERRORLEVEL%"=="0" goto mainEnd
|
||||||
|
|
||||||
|
:fail
|
||||||
|
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
|
||||||
|
rem the _cmd.exe /c_ return code!
|
||||||
|
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
|
||||||
|
exit /b 1
|
||||||
|
|
||||||
|
:mainEnd
|
||||||
|
if "%OS%"=="Windows_NT" endlocal
|
||||||
|
|
||||||
|
:omega
|
||||||
BIN
aws/s3/images/logo.png
Normal file
BIN
aws/s3/images/logo.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 60 KiB |
BIN
aws/s3/images/usage.gif
Normal file
BIN
aws/s3/images/usage.gif
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 2.0 MiB |
1
aws/s3/settings.gradle
Normal file
1
aws/s3/settings.gradle
Normal file
@@ -0,0 +1 @@
|
|||||||
|
rootProject.name = 's3'
|
||||||
38
aws/s3/src/main/java/io/jgoerner/s3/S3Application.java
Normal file
38
aws/s3/src/main/java/io/jgoerner/s3/S3Application.java
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
package io.jgoerner.s3;
|
||||||
|
|
||||||
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
|
import org.springframework.boot.SpringApplication;
|
||||||
|
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||||
|
import org.springframework.boot.autoconfigure.thymeleaf.ThymeleafProperties;
|
||||||
|
import org.springframework.cloud.aws.autoconfigure.context.ContextInstanceDataAutoConfiguration;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.thymeleaf.templateresolver.FileTemplateResolver;
|
||||||
|
import org.thymeleaf.templateresolver.ITemplateResolver;
|
||||||
|
|
||||||
|
@SpringBootApplication(exclude = ContextInstanceDataAutoConfiguration.class)
|
||||||
|
public class S3Application {
|
||||||
|
|
||||||
|
private final ThymeleafProperties properties;
|
||||||
|
|
||||||
|
@Value("${spring.thymeleaf.templates_root:}")
|
||||||
|
private String templatesRoot;
|
||||||
|
|
||||||
|
public S3Application(ThymeleafProperties properties) {
|
||||||
|
this.properties = properties;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
SpringApplication.run(S3Application.class, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public ITemplateResolver defaultTemplateResolver() {
|
||||||
|
FileTemplateResolver resolver = new FileTemplateResolver();
|
||||||
|
resolver.setSuffix(properties.getSuffix());
|
||||||
|
resolver.setPrefix(templatesRoot);
|
||||||
|
resolver.setTemplateMode(properties.getMode());
|
||||||
|
resolver.setCacheable(properties.isCache());
|
||||||
|
return resolver;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
128
aws/s3/src/main/java/io/jgoerner/s3/adapter/in/RestApi.java
Normal file
128
aws/s3/src/main/java/io/jgoerner/s3/adapter/in/RestApi.java
Normal file
@@ -0,0 +1,128 @@
|
|||||||
|
package io.jgoerner.s3.adapter.in;
|
||||||
|
|
||||||
|
import io.jgoerner.s3.application.port.in.object.*;
|
||||||
|
import io.jgoerner.s3.application.port.in.space.*;
|
||||||
|
import io.jgoerner.s3.domain.Object;
|
||||||
|
import io.jgoerner.s3.domain.ObjectPartial;
|
||||||
|
import io.jgoerner.s3.domain.Space;
|
||||||
|
import io.jgoerner.s3.domain.SpacePartial;
|
||||||
|
import lombok.SneakyThrows;
|
||||||
|
import lombok.extern.log4j.Log4j2;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
|
import java.net.URL;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("api")
|
||||||
|
@Log4j2
|
||||||
|
public class RestApi {
|
||||||
|
|
||||||
|
private final CreateSpace spaceCreator;
|
||||||
|
private final GetAllSpaces allSpaceGetter;
|
||||||
|
private final RemoveSpace spaceRemover;
|
||||||
|
private final GetAllObjects allObjectsInSpaceGetter;
|
||||||
|
private final UploadObject objectUploader;
|
||||||
|
private final UpdateObject objectUpdater;
|
||||||
|
private final RemoveObject objectDeleter;
|
||||||
|
private final ForceRemoveSpace forceSpaceRemover;
|
||||||
|
private final CreateLink linkCreator;
|
||||||
|
private final SetTTL ttlUpdater;
|
||||||
|
private final RemoveTTL ttlRemover;
|
||||||
|
|
||||||
|
public RestApi(
|
||||||
|
CreateSpace spaceCreator,
|
||||||
|
GetAllSpaces allSpaceGetter,
|
||||||
|
RemoveSpace spaceRemover,
|
||||||
|
GetAllObjects allObjectsInSpaceGetter,
|
||||||
|
UploadObject objectUploader,
|
||||||
|
UpdateObject objectUpdater,
|
||||||
|
RemoveObject objectDeleter,
|
||||||
|
ForceRemoveSpace forceSpaceRemover,
|
||||||
|
CreateLink linkCreator,
|
||||||
|
SetTTL ttlUpdater,
|
||||||
|
RemoveTTL ttlRemover) {
|
||||||
|
this.spaceCreator = spaceCreator;
|
||||||
|
this.allSpaceGetter = allSpaceGetter;
|
||||||
|
this.spaceRemover = spaceRemover;
|
||||||
|
this.allObjectsInSpaceGetter = allObjectsInSpaceGetter;
|
||||||
|
this.objectUploader = objectUploader;
|
||||||
|
this.objectUpdater = objectUpdater;
|
||||||
|
this.objectDeleter = objectDeleter;
|
||||||
|
this.forceSpaceRemover = forceSpaceRemover;
|
||||||
|
this.linkCreator = linkCreator;
|
||||||
|
this.ttlUpdater = ttlUpdater;
|
||||||
|
this.ttlRemover = ttlRemover;
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/space")
|
||||||
|
List<Space> getSpaces() {
|
||||||
|
return allSpaceGetter.getAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping("/space/{space}")
|
||||||
|
Space postSpace(@PathVariable String space) {
|
||||||
|
return spaceCreator.create(space);
|
||||||
|
}
|
||||||
|
|
||||||
|
@DeleteMapping("/space/{space}")
|
||||||
|
void deleteSpace(@PathVariable String space, @RequestParam Optional<Boolean> force) {
|
||||||
|
log.info("Got the value " + force);
|
||||||
|
force.ifPresentOrElse(
|
||||||
|
value -> {
|
||||||
|
if (value) {
|
||||||
|
forceSpaceRemover.forceRemove(space);
|
||||||
|
} else {
|
||||||
|
spaceRemover.remove(space);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
() -> spaceRemover.remove(space));
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/space/{space}/object")
|
||||||
|
List<Object> getObjectsInSpace(@PathVariable String space) {
|
||||||
|
return allObjectsInSpaceGetter.getAllObjects(space);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SneakyThrows
|
||||||
|
@PostMapping("/space/{space}/object")
|
||||||
|
Object postObject(
|
||||||
|
@PathVariable String space,
|
||||||
|
@RequestParam("file") MultipartFile file,
|
||||||
|
@RequestParam(required = false, name = "name") String name) {
|
||||||
|
var key = name != null ? name : file.getOriginalFilename();
|
||||||
|
return objectUploader.upload(space, key, file.getInputStream());
|
||||||
|
}
|
||||||
|
|
||||||
|
@PatchMapping("/space/{space}/object/{key}")
|
||||||
|
Object patchObject(
|
||||||
|
@PathVariable String space, @PathVariable String key, @RequestBody ObjectPartial body) {
|
||||||
|
log.info("Got the partial " + body);
|
||||||
|
return objectUpdater.update(space, key, body);
|
||||||
|
}
|
||||||
|
|
||||||
|
@DeleteMapping("/space/{space}/object/{key}")
|
||||||
|
void deleteObject(@PathVariable String space, @PathVariable String key) {
|
||||||
|
objectDeleter.delete(space, key);
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping("/space/{space}/object/{key}/url")
|
||||||
|
URL createLink(
|
||||||
|
@PathVariable String space,
|
||||||
|
@PathVariable String key,
|
||||||
|
@RequestParam(required = false, name = "duration", defaultValue = "300") Long duration) {
|
||||||
|
return linkCreator.createLink(space, key, duration);
|
||||||
|
}
|
||||||
|
|
||||||
|
@PatchMapping("/space/{space}")
|
||||||
|
void patchSpace(@PathVariable String space, @RequestBody(required = false) SpacePartial body) {
|
||||||
|
log.info("got " + body);
|
||||||
|
if (body.getTtlInDays() > 1) {
|
||||||
|
ttlUpdater.setTTL(space, body.getTtlInDays());
|
||||||
|
} else {
|
||||||
|
ttlRemover.removeTTL(space);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
126
aws/s3/src/main/java/io/jgoerner/s3/adapter/in/View.java
Normal file
126
aws/s3/src/main/java/io/jgoerner/s3/adapter/in/View.java
Normal file
@@ -0,0 +1,126 @@
|
|||||||
|
package io.jgoerner.s3.adapter.in;
|
||||||
|
|
||||||
|
import io.jgoerner.s3.domain.ObjectPartial;
|
||||||
|
import io.jgoerner.s3.domain.SpacePartial;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.extern.log4j.Log4j2;
|
||||||
|
import org.springframework.stereotype.Controller;
|
||||||
|
import org.springframework.ui.Model;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
import org.springframework.web.servlet.mvc.support.RedirectAttributes;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
@Log4j2
|
||||||
|
@Controller()
|
||||||
|
public class View {
|
||||||
|
|
||||||
|
private final RestApi api;
|
||||||
|
|
||||||
|
public View(RestApi api) {
|
||||||
|
this.api = api;
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("space")
|
||||||
|
String space(Model model) {
|
||||||
|
var spaces = api.getSpaces();
|
||||||
|
model.addAttribute("spaces", spaces);
|
||||||
|
return "space-overview";
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("space/{name}")
|
||||||
|
String spaceDetail(@PathVariable String name, Model model) {
|
||||||
|
model.addAllAttributes(Map.of("space", name, "objects", api.getObjectsInSpace(name)));
|
||||||
|
return "space-detail";
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("space/{space}/make-public/{key}")
|
||||||
|
String makePublic(
|
||||||
|
@PathVariable String space, @PathVariable String key, RedirectAttributes redirectAttributes) {
|
||||||
|
api.patchObject(space, key, new ObjectPartial(true));
|
||||||
|
redirectAttributes.addFlashAttribute("success", "Made object public");
|
||||||
|
return "redirect:/space/" + space;
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("space/{space}/make-private/{key}")
|
||||||
|
String makePrivate(
|
||||||
|
@PathVariable String space, @PathVariable String key, RedirectAttributes redirectAttributes) {
|
||||||
|
api.patchObject(space, key, new ObjectPartial(false));
|
||||||
|
redirectAttributes.addFlashAttribute("success", "Made object private");
|
||||||
|
return "redirect:/space/" + space;
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("space/{space}/delete/{key}")
|
||||||
|
String deleteObject(@PathVariable String space, @PathVariable String key) {
|
||||||
|
api.deleteObject(space, key);
|
||||||
|
return "redirect:/space/" + space;
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/space/{space}/object-form")
|
||||||
|
String objectNew(@PathVariable String space, Model model) {
|
||||||
|
model.addAllAttributes(Map.of("space", space, "object", new ObjectForm()));
|
||||||
|
return "object-form";
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping("/space/{space}/object-form")
|
||||||
|
String postObject(
|
||||||
|
@PathVariable String space,
|
||||||
|
@RequestParam("file") MultipartFile file,
|
||||||
|
@ModelAttribute ObjectForm form,
|
||||||
|
RedirectAttributes redirectAttributes) {
|
||||||
|
api.postObject(space, file, form.getName());
|
||||||
|
redirectAttributes.addFlashAttribute("success", "Successfully uploaded " + form.getName());
|
||||||
|
return "redirect:/space/" + space;
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/space/{space}/magic/{key}")
|
||||||
|
String magicLink(
|
||||||
|
@PathVariable String space, @PathVariable String key, RedirectAttributes redirectAttributes) {
|
||||||
|
var link = api.createLink(space, key, 15L);
|
||||||
|
redirectAttributes.addFlashAttribute("warn", link);
|
||||||
|
return "redirect:/space/" + space;
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("space-form")
|
||||||
|
String spaceNew(Model model) {
|
||||||
|
model.addAttribute("space", new SpaceForm());
|
||||||
|
return "space-form";
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping("space-form")
|
||||||
|
String newSpace(@ModelAttribute SpaceForm form, RedirectAttributes redirectAttributes) {
|
||||||
|
api.postSpace(form.getName());
|
||||||
|
redirectAttributes.addFlashAttribute("success", "Successfully created " + form.getName());
|
||||||
|
return "redirect:space";
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("space/temporary/{name}")
|
||||||
|
String makeTemporary(@PathVariable String name) {
|
||||||
|
api.patchSpace(name, new SpacePartial(2));
|
||||||
|
return "redirect:/space";
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("space/permanent/{name}")
|
||||||
|
String makePermanent(@PathVariable String name) {
|
||||||
|
api.patchSpace(name, new SpacePartial(-1));
|
||||||
|
return "redirect:/space";
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("space/delete/{name}")
|
||||||
|
String delete(@PathVariable String name) {
|
||||||
|
api.deleteSpace(name, Optional.of(true));
|
||||||
|
return "redirect:/space";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Data
|
||||||
|
class SpaceForm {
|
||||||
|
String name;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Data
|
||||||
|
class ObjectForm {
|
||||||
|
String name;
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
package io.jgoerner.s3.adapter.out.h2;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
import lombok.Setter;
|
||||||
|
|
||||||
|
import javax.persistence.Entity;
|
||||||
|
import javax.persistence.Id;
|
||||||
|
|
||||||
|
@Entity
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
|
public class SpaceEntity {
|
||||||
|
@Id private String name;
|
||||||
|
private String bucket;
|
||||||
|
private Integer ttl;
|
||||||
|
}
|
||||||
@@ -0,0 +1,69 @@
|
|||||||
|
package io.jgoerner.s3.adapter.out.h2;
|
||||||
|
|
||||||
|
import io.jgoerner.s3.application.port.out.space.*;
|
||||||
|
import io.jgoerner.s3.domain.Space;
|
||||||
|
import lombok.extern.log4j.Log4j2;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
@Component
|
||||||
|
@Log4j2
|
||||||
|
public class SpacePersistenceHandler
|
||||||
|
implements CheckSpaceExistence,
|
||||||
|
SaveSpace,
|
||||||
|
RetrieveAllSpaces,
|
||||||
|
ResolveSpaceName,
|
||||||
|
DeleteSpace,
|
||||||
|
RetrieveSpaceByName {
|
||||||
|
|
||||||
|
private final SpaceRepository spaceRepository;
|
||||||
|
|
||||||
|
public SpacePersistenceHandler(SpaceRepository spaceRepository) {
|
||||||
|
this.spaceRepository = spaceRepository;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean doesExist(String name) {
|
||||||
|
return this.spaceRepository.findById(name).isPresent();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Space save(Space name) {
|
||||||
|
this.spaceRepository.save(SpacePersistenceHandler.mapPojoToJpa(name));
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<Space> findAll() {
|
||||||
|
return spaceRepository.findAll().stream()
|
||||||
|
.map(SpacePersistenceHandler::mapJpaToPojo)
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
|
||||||
|
private static SpaceEntity mapPojoToJpa(Space space) {
|
||||||
|
return new SpaceEntity(space.getName(), space.getBucket(), space.getTtl());
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Space mapJpaToPojo(SpaceEntity entity) {
|
||||||
|
return new Space(entity.getName(), entity.getBucket(), entity.getTtl());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String resolve(String name) {
|
||||||
|
var bucket = spaceRepository.findById(name).get().getBucket();
|
||||||
|
log.info("Space " + name + " was resolved to " + bucket);
|
||||||
|
return bucket;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void delete(String name) {
|
||||||
|
spaceRepository.deleteById(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Space retrieveByName(String name) {
|
||||||
|
return mapJpaToPojo(spaceRepository.findById(name).orElseThrow());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
package io.jgoerner.s3.adapter.out.h2;
|
||||||
|
|
||||||
|
import org.springframework.data.jpa.repository.JpaRepository;
|
||||||
|
import org.springframework.stereotype.Repository;
|
||||||
|
|
||||||
|
@Repository
|
||||||
|
public interface SpaceRepository extends JpaRepository<SpaceEntity, String> {}
|
||||||
@@ -0,0 +1,143 @@
|
|||||||
|
package io.jgoerner.s3.adapter.out.s3;
|
||||||
|
|
||||||
|
import com.amazonaws.services.s3.AmazonS3Client;
|
||||||
|
import com.amazonaws.services.s3.model.*;
|
||||||
|
import com.amazonaws.services.s3.model.lifecycle.LifecycleFilter;
|
||||||
|
import com.amazonaws.waiters.WaiterParameters;
|
||||||
|
import io.jgoerner.s3.application.port.out.bucket.*;
|
||||||
|
import io.jgoerner.s3.application.port.out.object.*;
|
||||||
|
import io.jgoerner.s3.domain.Object;
|
||||||
|
import lombok.extern.log4j.Log4j2;
|
||||||
|
import org.springframework.stereotype.Repository;
|
||||||
|
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.net.URL;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
@Repository
|
||||||
|
@Log4j2
|
||||||
|
public class S3Repository
|
||||||
|
implements CreateBucket,
|
||||||
|
DeleteBucket,
|
||||||
|
ListObjects,
|
||||||
|
SaveObject,
|
||||||
|
MakeObjectPublic,
|
||||||
|
MakeObjectPrivate,
|
||||||
|
DeleteObject,
|
||||||
|
CreatePresignedUrl,
|
||||||
|
SetVisibilityInObjectLifecycle,
|
||||||
|
RemoveVisibilityInObjectLifecycle {
|
||||||
|
|
||||||
|
private final AmazonS3Client s3Client;
|
||||||
|
|
||||||
|
public S3Repository(AmazonS3Client s3Client) {
|
||||||
|
this.s3Client = s3Client;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void create(String bucket) {
|
||||||
|
// send bucket creation request
|
||||||
|
s3Client.createBucket(bucket);
|
||||||
|
log.info("Request to create " + bucket + " sent");
|
||||||
|
|
||||||
|
// assure that bucket is available
|
||||||
|
s3Client.waiters().bucketExists().run(new WaiterParameters<>(new HeadBucketRequest(bucket)));
|
||||||
|
log.info("Bucket " + bucket + " is ready");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void delete(String bucket) {
|
||||||
|
// send deletion request
|
||||||
|
s3Client.deleteBucket(bucket);
|
||||||
|
log.info("Request to delete " + bucket + " sent");
|
||||||
|
|
||||||
|
// assure bucket is deleted
|
||||||
|
s3Client.waiters().bucketNotExists().run(new WaiterParameters(new HeadBucketRequest(bucket)));
|
||||||
|
log.info("Bucket " + bucket + " is deleted");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<Object> listObjectsInBucket(String bucket) {
|
||||||
|
var items =
|
||||||
|
s3Client.listObjectsV2(bucket).getObjectSummaries().stream()
|
||||||
|
.parallel()
|
||||||
|
.map(S3ObjectSummary::getKey)
|
||||||
|
.map(key -> mapS3ToObject(bucket, key))
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
|
log.info("Found " + items.size() + " objects in the bucket " + bucket);
|
||||||
|
return items;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object safe(String bucket, String key, String name, InputStream payload) {
|
||||||
|
var metadata = new ObjectMetadata();
|
||||||
|
metadata.addUserMetadata("name", name);
|
||||||
|
s3Client.putObject(bucket, key, payload, metadata);
|
||||||
|
log.info("Sent the request");
|
||||||
|
return Object.builder().name(name).key(key).url(s3Client.getUrl(bucket, key)).build();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void makePublic(String bucket, String key) {
|
||||||
|
s3Client.setObjectAcl(bucket, key, CannedAccessControlList.PublicRead);
|
||||||
|
log.info("Sent request to make object in bucket " + bucket + " with key " + key + " public");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void makePrivate(String bucket, String key) {
|
||||||
|
s3Client.setObjectAcl(bucket, key, CannedAccessControlList.BucketOwnerFullControl);
|
||||||
|
log.info("Sent request to make object in bucket " + bucket + " with key " + key + " private");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void delete(String bucket, String key) {
|
||||||
|
s3Client.deleteObject(bucket, key);
|
||||||
|
log.info("Sent request to delete file with key " + key + " in bucket " + bucket);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public URL createURL(String bucket, String key, Long duration) {
|
||||||
|
var date = new Date(new Date().getTime() + duration * 1000); // 1 s * 1000 ms/s
|
||||||
|
var url = s3Client.generatePresignedUrl(bucket, key, date);
|
||||||
|
log.info("Generated the signature " + url);
|
||||||
|
return url;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setVisibility(String bucket, Integer ttlInDays) {
|
||||||
|
s3Client.setBucketLifecycleConfiguration(
|
||||||
|
bucket,
|
||||||
|
new BucketLifecycleConfiguration()
|
||||||
|
.withRules(
|
||||||
|
new BucketLifecycleConfiguration.Rule()
|
||||||
|
.withId("custom-expiration-id")
|
||||||
|
.withFilter(new LifecycleFilter())
|
||||||
|
.withStatus(BucketLifecycleConfiguration.ENABLED)
|
||||||
|
.withExpirationInDays(ttlInDays)));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void removeVisibility(String bucket) {
|
||||||
|
s3Client.deleteBucketLifecycleConfiguration(bucket);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Object mapS3ToObject(String bucket, String key) {
|
||||||
|
|
||||||
|
return Object.builder()
|
||||||
|
.name(s3Client.getObjectMetadata(bucket, key).getUserMetaDataOf("name"))
|
||||||
|
.key(key)
|
||||||
|
.url(s3Client.getUrl(bucket, key))
|
||||||
|
.isPublic(
|
||||||
|
s3Client.getObjectAcl(bucket, key).getGrantsAsList().stream()
|
||||||
|
.anyMatch(grant -> grant.equals(S3Repository.publicObjectReadGrant())))
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Grant publicObjectReadGrant() {
|
||||||
|
return new Grant(
|
||||||
|
GroupGrantee.parseGroupGrantee(GroupGrantee.AllUsers.getIdentifier()), Permission.Read);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
package io.jgoerner.s3.application.port.in.object;
|
||||||
|
|
||||||
|
import java.net.URL;
|
||||||
|
|
||||||
|
public interface CreateLink {
|
||||||
|
URL createLink(String space, String key, Long duration);
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
package io.jgoerner.s3.application.port.in.object;
|
||||||
|
|
||||||
|
import io.jgoerner.s3.domain.Object;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public interface GetAllObjects {
|
||||||
|
List<Object> getAllObjects(String space);
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
package io.jgoerner.s3.application.port.in.object;
|
||||||
|
|
||||||
|
public interface RemoveObject {
|
||||||
|
void delete(String space, String key);
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
package io.jgoerner.s3.application.port.in.object;
|
||||||
|
|
||||||
|
import io.jgoerner.s3.domain.ObjectPartial;
|
||||||
|
import io.jgoerner.s3.domain.Object;
|
||||||
|
|
||||||
|
public interface UpdateObject {
|
||||||
|
Object update(String space, String key, ObjectPartial updates);
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
package io.jgoerner.s3.application.port.in.object;
|
||||||
|
|
||||||
|
import io.jgoerner.s3.domain.Object;
|
||||||
|
|
||||||
|
import java.io.InputStream;
|
||||||
|
|
||||||
|
public interface UploadObject {
|
||||||
|
Object upload(String space, String name, InputStream payload);
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
package io.jgoerner.s3.application.port.in.space;
|
||||||
|
|
||||||
|
import io.jgoerner.s3.domain.Space;
|
||||||
|
|
||||||
|
public interface CreateSpace {
|
||||||
|
Space create(String name);
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
package io.jgoerner.s3.application.port.in.space;
|
||||||
|
|
||||||
|
public interface ForceRemoveSpace {
|
||||||
|
void forceRemove(String space);
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
package io.jgoerner.s3.application.port.in.space;
|
||||||
|
|
||||||
|
import io.jgoerner.s3.domain.Space;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public interface GetAllSpaces {
|
||||||
|
List<Space> getAll();
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
package io.jgoerner.s3.application.port.in.space;
|
||||||
|
|
||||||
|
public interface RemoveSpace {
|
||||||
|
void remove(String space);
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
package io.jgoerner.s3.application.port.in.space;
|
||||||
|
|
||||||
|
public interface RemoveTTL {
|
||||||
|
void removeTTL(String space);
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
package io.jgoerner.s3.application.port.in.space;
|
||||||
|
|
||||||
|
public interface SetTTL {
|
||||||
|
void setTTL(String space, Integer ttlInDays);
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
package io.jgoerner.s3.application.port.out.bucket;
|
||||||
|
|
||||||
|
public interface CreateBucket {
|
||||||
|
void create(String bucket);
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
package io.jgoerner.s3.application.port.out.bucket;
|
||||||
|
|
||||||
|
public interface DeleteBucket {
|
||||||
|
void delete(String bucket);
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
package io.jgoerner.s3.application.port.out.bucket;
|
||||||
|
|
||||||
|
import io.jgoerner.s3.domain.Object;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public interface ListObjects {
|
||||||
|
List<Object> listObjectsInBucket(String bucket);
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
package io.jgoerner.s3.application.port.out.bucket;
|
||||||
|
|
||||||
|
public interface RemoveVisibilityInObjectLifecycle {
|
||||||
|
void removeVisibility(String bucket);
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
package io.jgoerner.s3.application.port.out.bucket;
|
||||||
|
|
||||||
|
public interface SetVisibilityInObjectLifecycle {
|
||||||
|
void setVisibility(String bucket, Integer ttlInDays);
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
package io.jgoerner.s3.application.port.out.object;
|
||||||
|
|
||||||
|
import java.net.URL;
|
||||||
|
|
||||||
|
public interface CreatePresignedUrl {
|
||||||
|
URL createURL(String bucket, String key, Long duration);
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
package io.jgoerner.s3.application.port.out.object;
|
||||||
|
|
||||||
|
public interface DeleteObject {
|
||||||
|
void delete(String bucket, String key);
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
package io.jgoerner.s3.application.port.out.object;
|
||||||
|
|
||||||
|
public interface MakeObjectPrivate {
|
||||||
|
void makePrivate(String bucket, String key);
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
package io.jgoerner.s3.application.port.out.object;
|
||||||
|
|
||||||
|
public interface MakeObjectPublic {
|
||||||
|
void makePublic(String bucket, String key);
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
package io.jgoerner.s3.application.port.out.object;
|
||||||
|
|
||||||
|
import io.jgoerner.s3.domain.Object;
|
||||||
|
|
||||||
|
import java.io.InputStream;
|
||||||
|
|
||||||
|
public interface SaveObject {
|
||||||
|
Object safe(String bucket, String key, String name, InputStream payload);
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
package io.jgoerner.s3.application.port.out.space;
|
||||||
|
|
||||||
|
public interface CheckSpaceExistence {
|
||||||
|
boolean doesExist(String name);
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
package io.jgoerner.s3.application.port.out.space;
|
||||||
|
|
||||||
|
public interface DeleteSpace {
|
||||||
|
void delete(String name);
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
package io.jgoerner.s3.application.port.out.space;
|
||||||
|
|
||||||
|
public interface ResolveSpaceName {
|
||||||
|
String resolve(String name);
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
package io.jgoerner.s3.application.port.out.space;
|
||||||
|
|
||||||
|
import io.jgoerner.s3.domain.Space;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public interface RetrieveAllSpaces {
|
||||||
|
List<Space> findAll();
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
package io.jgoerner.s3.application.port.out.space;
|
||||||
|
|
||||||
|
import io.jgoerner.s3.domain.Space;
|
||||||
|
|
||||||
|
public interface RetrieveSpaceByName {
|
||||||
|
Space retrieveByName(String name);
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
package io.jgoerner.s3.application.port.out.space;
|
||||||
|
|
||||||
|
import io.jgoerner.s3.domain.Space;
|
||||||
|
|
||||||
|
public interface SaveSpace {
|
||||||
|
Space save(Space name);
|
||||||
|
}
|
||||||
@@ -0,0 +1,105 @@
|
|||||||
|
package io.jgoerner.s3.application.service;
|
||||||
|
|
||||||
|
import io.jgoerner.s3.application.port.in.object.CreateLink;
|
||||||
|
import io.jgoerner.s3.application.port.in.object.RemoveObject;
|
||||||
|
import io.jgoerner.s3.application.port.in.object.UpdateObject;
|
||||||
|
import io.jgoerner.s3.application.port.in.object.UploadObject;
|
||||||
|
import io.jgoerner.s3.application.port.out.object.*;
|
||||||
|
import io.jgoerner.s3.application.port.out.space.ResolveSpaceName;
|
||||||
|
import io.jgoerner.s3.domain.Object;
|
||||||
|
import io.jgoerner.s3.domain.ObjectPartial;
|
||||||
|
import lombok.extern.log4j.Log4j2;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.net.URL;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
@Log4j2
|
||||||
|
public class ObjectService implements UploadObject, UpdateObject, RemoveObject, CreateLink {
|
||||||
|
|
||||||
|
private final ResolveSpaceName bucketNameResolver;
|
||||||
|
private final SaveObject objectSaver;
|
||||||
|
private final MakeObjectPublic objectPublicMaker;
|
||||||
|
private final MakeObjectPrivate objectPrivateMaker;
|
||||||
|
private final DeleteObject objectDeleter;
|
||||||
|
private final CreatePresignedUrl presignedUrlCreator;
|
||||||
|
|
||||||
|
public ObjectService(
|
||||||
|
ResolveSpaceName bucketNameResolver,
|
||||||
|
SaveObject objectSaver,
|
||||||
|
MakeObjectPublic objectPublicMaker,
|
||||||
|
MakeObjectPrivate objectPrivateMaker,
|
||||||
|
DeleteObject objectDeleter,
|
||||||
|
CreatePresignedUrl presignedUrlCreator) {
|
||||||
|
this.bucketNameResolver = bucketNameResolver;
|
||||||
|
this.objectSaver = objectSaver;
|
||||||
|
this.objectPublicMaker = objectPublicMaker;
|
||||||
|
this.objectPrivateMaker = objectPrivateMaker;
|
||||||
|
this.objectDeleter = objectDeleter;
|
||||||
|
this.presignedUrlCreator = presignedUrlCreator;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object upload(String space, String name, InputStream payload) {
|
||||||
|
// check if bucket exists
|
||||||
|
var bucket = bucketNameResolver.resolve(space);
|
||||||
|
|
||||||
|
// generate a id & store in lookup table
|
||||||
|
var key = UUID.randomUUID().toString();
|
||||||
|
|
||||||
|
// save
|
||||||
|
log.info(
|
||||||
|
"Going to upload the file into to "
|
||||||
|
+ bucket
|
||||||
|
+ "/"
|
||||||
|
+ key
|
||||||
|
+ " with metadata name of "
|
||||||
|
+ name);
|
||||||
|
|
||||||
|
return objectSaver.safe(bucket, key, name, payload);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object update(String space, String key, ObjectPartial updates) {
|
||||||
|
var bucket = bucketNameResolver.resolve(space);
|
||||||
|
|
||||||
|
if (updates.getIsPublic() != null) {
|
||||||
|
if (updates.getIsPublic()) {
|
||||||
|
log.info("going to open up to public");
|
||||||
|
objectPublicMaker.makePublic(bucket, key);
|
||||||
|
} else {
|
||||||
|
log.info("going to remove public access");
|
||||||
|
objectPrivateMaker.makePrivate(bucket, key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void delete(String space, String key) {
|
||||||
|
var bucket = bucketNameResolver.resolve(space);
|
||||||
|
|
||||||
|
log.info("Going to delete the file with the key " + key + " in the bucket " + bucket);
|
||||||
|
|
||||||
|
objectDeleter.delete(bucket, key);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public URL createLink(String space, String key, Long duration) {
|
||||||
|
var bucket = bucketNameResolver.resolve(space);
|
||||||
|
|
||||||
|
log.info(
|
||||||
|
"Going to generate a link for the file "
|
||||||
|
+ key
|
||||||
|
+ " in bucket "
|
||||||
|
+ bucket
|
||||||
|
+ " with visibility duration of "
|
||||||
|
+ duration
|
||||||
|
+ " seconds");
|
||||||
|
|
||||||
|
return presignedUrlCreator.createURL(bucket, key, duration);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,155 @@
|
|||||||
|
package io.jgoerner.s3.application.service;
|
||||||
|
|
||||||
|
import io.jgoerner.s3.application.port.in.object.GetAllObjects;
|
||||||
|
import io.jgoerner.s3.application.port.in.object.RemoveObject;
|
||||||
|
import io.jgoerner.s3.application.port.in.space.*;
|
||||||
|
import io.jgoerner.s3.application.port.out.bucket.*;
|
||||||
|
import io.jgoerner.s3.application.port.out.object.DeleteObject;
|
||||||
|
import io.jgoerner.s3.application.port.out.space.*;
|
||||||
|
import io.jgoerner.s3.domain.Object;
|
||||||
|
import io.jgoerner.s3.domain.Space;
|
||||||
|
import lombok.extern.log4j.Log4j2;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
@Log4j2
|
||||||
|
public class SpaceService
|
||||||
|
implements CreateSpace,
|
||||||
|
GetAllSpaces,
|
||||||
|
GetAllObjects,
|
||||||
|
RemoveSpace,
|
||||||
|
ForceRemoveSpace,
|
||||||
|
SetTTL,
|
||||||
|
RemoveTTL {
|
||||||
|
|
||||||
|
private final CheckSpaceExistence spaceExistenceChecker;
|
||||||
|
private final SaveSpace spaceSaver;
|
||||||
|
private final RetrieveAllSpaces allSpaceRetriever;
|
||||||
|
private final CreateBucket bucketCreator;
|
||||||
|
private final ResolveSpaceName bucketNameResolver;
|
||||||
|
private final DeleteBucket bucketDeleter;
|
||||||
|
private final DeleteSpace spaceDeleter;
|
||||||
|
private final ListObjects objectLister;
|
||||||
|
private final DeleteObject objectDeleter;
|
||||||
|
private final SetVisibilityInObjectLifecycle objectLifecycleVisibilitySetter;
|
||||||
|
private final RemoveVisibilityInObjectLifecycle objectLifecycleVisibilityRemover;
|
||||||
|
private final RetrieveSpaceByName spaceByNameRetriever;
|
||||||
|
|
||||||
|
public SpaceService(
|
||||||
|
CheckSpaceExistence spaceExistenceChecker,
|
||||||
|
SaveSpace spaceSaver,
|
||||||
|
RetrieveAllSpaces allSpaceRetriever,
|
||||||
|
CreateBucket bucketCreator,
|
||||||
|
ResolveSpaceName bucketNameResolver,
|
||||||
|
DeleteBucket bucketDeleter,
|
||||||
|
DeleteSpace spaceDeleter,
|
||||||
|
ListObjects objectLister,
|
||||||
|
RemoveObject objectRemover,
|
||||||
|
DeleteObject objectDeleter,
|
||||||
|
SetVisibilityInObjectLifecycle objectLifecycleVisibilitySetter,
|
||||||
|
RemoveVisibilityInObjectLifecycle objectLifecycleVisibilityRemover,
|
||||||
|
RetrieveSpaceByName spaceByNameRetriever) {
|
||||||
|
this.spaceExistenceChecker = spaceExistenceChecker;
|
||||||
|
this.spaceSaver = spaceSaver;
|
||||||
|
this.allSpaceRetriever = allSpaceRetriever;
|
||||||
|
this.bucketNameResolver = bucketNameResolver;
|
||||||
|
this.bucketCreator = bucketCreator;
|
||||||
|
this.bucketDeleter = bucketDeleter;
|
||||||
|
this.spaceDeleter = spaceDeleter;
|
||||||
|
this.objectLister = objectLister;
|
||||||
|
this.objectDeleter = objectDeleter;
|
||||||
|
this.objectLifecycleVisibilitySetter = objectLifecycleVisibilitySetter;
|
||||||
|
this.objectLifecycleVisibilityRemover = objectLifecycleVisibilityRemover;
|
||||||
|
this.spaceByNameRetriever = spaceByNameRetriever;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Space create(String name) {
|
||||||
|
// check if bucket exists
|
||||||
|
if (spaceExistenceChecker.doesExist(name)) {
|
||||||
|
log.info("Space " + name + " does already exist");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// create
|
||||||
|
Space space = new Space(name, "spring-boot-s3-tutorial-" + UUID.randomUUID().toString(), null);
|
||||||
|
log.info("Mapped space to bucket " + space);
|
||||||
|
bucketCreator.create(space.getBucket());
|
||||||
|
|
||||||
|
// create bucket meta
|
||||||
|
return this.spaceSaver.save(space);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<Space> getAll() {
|
||||||
|
var buckets = allSpaceRetriever.findAll();
|
||||||
|
log.info("Found " + buckets.size() + " buckets");
|
||||||
|
return buckets;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<Object> getAllObjects(String space) {
|
||||||
|
// get bucket from H2
|
||||||
|
var bucket = bucketNameResolver.resolve(space);
|
||||||
|
|
||||||
|
// return all files in bucket
|
||||||
|
return objectLister.listObjectsInBucket(bucket);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void remove(String space) {
|
||||||
|
// get bucket from H2
|
||||||
|
var bucket = bucketNameResolver.resolve(space);
|
||||||
|
|
||||||
|
// delete from S3
|
||||||
|
bucketDeleter.delete(bucket);
|
||||||
|
|
||||||
|
// delete from H2
|
||||||
|
spaceDeleter.delete(space);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void forceRemove(String space) {
|
||||||
|
// get bucket from H2
|
||||||
|
var bucket = bucketNameResolver.resolve(space);
|
||||||
|
|
||||||
|
// empty bucket
|
||||||
|
getAllObjects(space).stream()
|
||||||
|
.peek(log::info)
|
||||||
|
.forEach(object -> objectDeleter.delete(bucket, object.getKey()));
|
||||||
|
|
||||||
|
// get rid of bucket
|
||||||
|
remove(space);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setTTL(String space, Integer ttlInDays) {
|
||||||
|
var bucket = bucketNameResolver.resolve(space);
|
||||||
|
log.info("Going to adjust the TTL for the bucket " + bucket + " to " + ttlInDays + " day(s)");
|
||||||
|
|
||||||
|
// S3
|
||||||
|
objectLifecycleVisibilitySetter.setVisibility(bucket, ttlInDays);
|
||||||
|
|
||||||
|
// H2
|
||||||
|
var spaceEntity = spaceByNameRetriever.retrieveByName(space);
|
||||||
|
spaceEntity.setTtl(ttlInDays);
|
||||||
|
spaceSaver.save(spaceEntity);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void removeTTL(String space) {
|
||||||
|
var bucket = bucketNameResolver.resolve(space);
|
||||||
|
log.info("Going to remove TTL policy for bucket " + bucket);
|
||||||
|
|
||||||
|
// S3
|
||||||
|
objectLifecycleVisibilityRemover.removeVisibility(bucket);
|
||||||
|
|
||||||
|
// H2
|
||||||
|
var spaceEntity = spaceByNameRetriever.retrieveByName(space);
|
||||||
|
spaceEntity.setTtl(null);
|
||||||
|
spaceSaver.save(spaceEntity);
|
||||||
|
}
|
||||||
|
}
|
||||||
15
aws/s3/src/main/java/io/jgoerner/s3/domain/Object.java
Normal file
15
aws/s3/src/main/java/io/jgoerner/s3/domain/Object.java
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
package io.jgoerner.s3.domain;
|
||||||
|
|
||||||
|
import lombok.Builder;
|
||||||
|
import lombok.Value;
|
||||||
|
|
||||||
|
import java.net.URL;
|
||||||
|
|
||||||
|
@Value
|
||||||
|
@Builder
|
||||||
|
public class Object {
|
||||||
|
String name;
|
||||||
|
String key;
|
||||||
|
URL url;
|
||||||
|
@Builder.Default boolean isPublic = false;
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
package io.jgoerner.s3.domain;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
@Data
|
||||||
|
public class ObjectPartial {
|
||||||
|
Boolean isPublic;
|
||||||
|
}
|
||||||
12
aws/s3/src/main/java/io/jgoerner/s3/domain/Space.java
Normal file
12
aws/s3/src/main/java/io/jgoerner/s3/domain/Space.java
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
package io.jgoerner.s3.domain;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
@AllArgsConstructor
|
||||||
|
@Data
|
||||||
|
public class Space {
|
||||||
|
String name;
|
||||||
|
String bucket;
|
||||||
|
Integer ttl;
|
||||||
|
}
|
||||||
12
aws/s3/src/main/java/io/jgoerner/s3/domain/SpacePartial.java
Normal file
12
aws/s3/src/main/java/io/jgoerner/s3/domain/SpacePartial.java
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
package io.jgoerner.s3.domain;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
@Data
|
||||||
|
public class SpacePartial {
|
||||||
|
Integer ttlInDays;
|
||||||
|
}
|
||||||
36
aws/s3/src/main/resources/application.yaml
Normal file
36
aws/s3/src/main/resources/application.yaml
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
spring:
|
||||||
|
datasource:
|
||||||
|
url: jdbc:h2:./data/metadb
|
||||||
|
username: admin
|
||||||
|
h2:
|
||||||
|
console:
|
||||||
|
enabled: true
|
||||||
|
path: /h2
|
||||||
|
jpa:
|
||||||
|
hibernate:
|
||||||
|
ddl-auto: update
|
||||||
|
# https://stackoverflow.com/questions/40057057/spring-boot-and-thymeleaf-hot-swap-templates-and-resources-once-again
|
||||||
|
thymeleaf:
|
||||||
|
cache: false
|
||||||
|
templates_root: src/main/resources/templates/
|
||||||
|
servlet:
|
||||||
|
multipart:
|
||||||
|
max-file-size: 15MB
|
||||||
|
max-request-size: 15MB
|
||||||
|
|
||||||
|
cloud:
|
||||||
|
aws:
|
||||||
|
region:
|
||||||
|
static: eu-central-1
|
||||||
|
stack:
|
||||||
|
auto: false
|
||||||
|
credentials:
|
||||||
|
profile-name: dev
|
||||||
|
|
||||||
|
# https://docs.spring.io/spring-cloud-aws/docs/2.2.3.RELEASE/reference/html/#amazon-sdk-configuration
|
||||||
|
logging:
|
||||||
|
level:
|
||||||
|
com:
|
||||||
|
amazonaws:
|
||||||
|
util:
|
||||||
|
EC2MetadataUtils: error
|
||||||
1
aws/s3/src/main/resources/static/css/bulma.min.css
vendored
Normal file
1
aws/s3/src/main/resources/static/css/bulma.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
7
aws/s3/src/main/resources/templates/fragments/head.html
Normal file
7
aws/s3/src/main/resources/templates/fragments/head.html
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
<head th:fragment="head" xmlns:th="http://www.w3.org/1999/xhtml">
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<title>File Share</title>
|
||||||
|
<link rel="stylesheet" th:href="@{/css/bulma.min.css}">
|
||||||
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.2/css/all.min.css">
|
||||||
|
</head>
|
||||||
43
aws/s3/src/main/resources/templates/fragments/navbar.html
Normal file
43
aws/s3/src/main/resources/templates/fragments/navbar.html
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
<nav class="navbar" th:fragment="navbar" xmlns:th="http://www.w3.org/1999/xhtml">
|
||||||
|
<div class=" container">
|
||||||
|
|
||||||
|
<div class="navbar-brand">
|
||||||
|
<a class="navbar-item" href="/">
|
||||||
|
<span class="icon-text">
|
||||||
|
<span class="icon">
|
||||||
|
<i class="fas fa-home"></i>
|
||||||
|
</span>
|
||||||
|
<span>Home</span>
|
||||||
|
</span>
|
||||||
|
</a>
|
||||||
|
<a class="navbar-item" th:href="@{~/space}">
|
||||||
|
<span class="icon-text">
|
||||||
|
<span class="icon">
|
||||||
|
<i class="fas fa-folder-open"></i>
|
||||||
|
</span>
|
||||||
|
<span>Spaces</span>
|
||||||
|
</span>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<div class="navbar-menu">
|
||||||
|
<div class="navbar-end">
|
||||||
|
<a class="navbar-item" href="h2" target="_blank">
|
||||||
|
<span class="icon-text">
|
||||||
|
<span class="icon">
|
||||||
|
<i class="fas fa-database"></i>
|
||||||
|
</span>
|
||||||
|
<span>DB</span>
|
||||||
|
</span>
|
||||||
|
</a>
|
||||||
|
<span class="navbar-item">
|
||||||
|
<a class="button is-primary" href="https://github.com/jgoerner" target="_blank">
|
||||||
|
<span class="icon">
|
||||||
|
<i class="fab fa-github"></i>
|
||||||
|
</span>
|
||||||
|
<span>GitHub</span>
|
||||||
|
</a>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
<script th:inline="javascript" th:fragment="script" xmlns:th="http://www.w3.org/1999/xhtml">
|
||||||
|
document.addEventListener('DOMContentLoaded', () => {
|
||||||
|
(document.querySelectorAll('.notification .delete') || []).forEach(($delete) => {
|
||||||
|
var $notification = $delete.parentNode;
|
||||||
|
|
||||||
|
$delete.addEventListener('click', () => {
|
||||||
|
$notification.parentNode.removeChild($notification);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div th:fragment="notification" xmlns:th="http://www.w3.org/1999/xhtml">
|
||||||
|
|
||||||
|
<div class="notification" th:if="${success}">
|
||||||
|
<button class="delete"></button>
|
||||||
|
<p class="has-text-grey-light" th:text="${success}"/>
|
||||||
|
</div>
|
||||||
|
<div class="notification is-warning is-light" th:if="${warn}">
|
||||||
|
<button class="delete"></button>
|
||||||
|
<p th:text="${warn}"/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
24
aws/s3/src/main/resources/templates/index.html
Normal file
24
aws/s3/src/main/resources/templates/index.html
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html xmlns="http://www.w3.org/1999/xhtml"
|
||||||
|
xmlns:th="http://www.thymeleaf.org">
|
||||||
|
<head th:replace="fragments/head :: head"/>
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<div th:replace="fragments/navbar :: navbar"/>
|
||||||
|
|
||||||
|
<section class="hero is-primary is-fullheight-with-navbar is-bold">
|
||||||
|
<div class="hero-body">
|
||||||
|
<div class="container has-text-centered">
|
||||||
|
<h1 class="title">
|
||||||
|
Private File Share
|
||||||
|
</h1>
|
||||||
|
<h2 class="subtitle">
|
||||||
|
powered by Spring Boot & Simple Storage Service (S3)
|
||||||
|
</h2>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
49
aws/s3/src/main/resources/templates/object-form.html
Normal file
49
aws/s3/src/main/resources/templates/object-form.html
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html xmlns="http://www.w3.org/1999/xhtml"
|
||||||
|
xmlns:th="http://www.thymeleaf.org">
|
||||||
|
<head th:replace="fragments/head :: head"/>
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<div th:replace="fragments/navbar :: navbar"/>
|
||||||
|
|
||||||
|
<section class="hero is-fullheight-with-navbar is-primary is-bold">
|
||||||
|
|
||||||
|
<section class="hero-body columns is-centered m-0">
|
||||||
|
<div class="column is-half ">
|
||||||
|
<form action="#" th:action="@{/space/__${space}__/object-form}" th:object="${object}" method="post"
|
||||||
|
enctype="multipart/form-data">
|
||||||
|
<div class="field">
|
||||||
|
<div class="control has-icons-left">
|
||||||
|
<input class="input is-medium" type="text" th:field="*{name}" placeholder="Name of your File">
|
||||||
|
<span class="icon is-small is-left">
|
||||||
|
<i class="fas fa-heading"></i>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<p class="help">Human readable name of the file</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="field">
|
||||||
|
<div class="control">
|
||||||
|
<input type="file" name="file"/>
|
||||||
|
</div>
|
||||||
|
<p class="help">Human readable name of the file</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="field is-grouped is-grouped-centered">
|
||||||
|
<p class="control">
|
||||||
|
<button class="button is-primary is-outlined is-inverted" type="submit">
|
||||||
|
Submit
|
||||||
|
</button>
|
||||||
|
</p>
|
||||||
|
<p class="control">
|
||||||
|
<a class="button is-primary is-outlined is-inverted" href="#">
|
||||||
|
Cancel
|
||||||
|
</a>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</section>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
81
aws/s3/src/main/resources/templates/space-detail.html
Normal file
81
aws/s3/src/main/resources/templates/space-detail.html
Normal file
@@ -0,0 +1,81 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html xmlns="http://www.w3.org/1999/xhtml"
|
||||||
|
xmlns:th="http://www.thymeleaf.org">
|
||||||
|
<head th:replace="fragments/head :: head"/>
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<div th:replace="fragments/navbar :: navbar"/>
|
||||||
|
|
||||||
|
<section class="hero is-fullheight-with-navbar is-primary is-bold">
|
||||||
|
|
||||||
|
<section class="hero-body columns is-centered m-0">
|
||||||
|
<div class="column is-half has-text-centered">
|
||||||
|
<div th:replace="fragments/notification :: notification"/>
|
||||||
|
<div th:each="object: ${objects}" class="card m-4">
|
||||||
|
<div class="card-content">
|
||||||
|
<div class="media">
|
||||||
|
<div class="media-content">
|
||||||
|
<p class="title is-4 has-text-dark" th:text="${object.name}"/>
|
||||||
|
<p class="subtitle is-6 has-text-grey-light mb-1" th:text="${object.key}"/>
|
||||||
|
<p th:if="${object.isPublic}" class="subtitle is-6 has-text-grey-light">
|
||||||
|
Publicly accessible
|
||||||
|
</p>
|
||||||
|
<p th:unless="${object.isPublic}" class="subtitle is-6 has-text-grey-light">Private</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="card-footer">
|
||||||
|
<a th:href="${object.url}" target="_blank"
|
||||||
|
class="card-footer-item has-text-info">
|
||||||
|
<span class="icon is-small pr-3">
|
||||||
|
<i class="fas fa-download"></i>
|
||||||
|
</span>
|
||||||
|
<span>Download</span>
|
||||||
|
</a>
|
||||||
|
<a th:if="${object.isPublic}" th:href="@{~/space/__${space}__/make-private/__${object.key}__}"
|
||||||
|
class="card-footer-item has-text-info">
|
||||||
|
<span class="icon is-small pr-3">
|
||||||
|
<i class="fas fa-eye-slash"></i>
|
||||||
|
</span>
|
||||||
|
<span>Make Private</span>
|
||||||
|
</a>
|
||||||
|
<a th:unless="${object.isPublic}" th:href="@{~/space/__${space}__/make-public/__${object.key}__}"
|
||||||
|
class="card-footer-item has-text-info">
|
||||||
|
<span class="icon is-small pr-3">
|
||||||
|
<i class="fas fa-eye"></i>
|
||||||
|
</span>
|
||||||
|
<span>Make Public</span>
|
||||||
|
</a>
|
||||||
|
<a th:href="@{~/space/__${space}__/magic/__${object.key}__}"
|
||||||
|
class="card-footer-item has-text-info">
|
||||||
|
<span class="icon is-small pr-3">
|
||||||
|
<i class="fas fa-magic"></i>
|
||||||
|
</span>
|
||||||
|
<span>Magic Link</span>
|
||||||
|
</a>
|
||||||
|
<a th:href="@{~/space/__${space}__/delete/__${object.key}__}"
|
||||||
|
class="card-footer-item has-text-danger">
|
||||||
|
<span class="icon is-small pr-3">
|
||||||
|
<i class="fas fa-trash-alt"></i>
|
||||||
|
</span>
|
||||||
|
<span>Delete</span>
|
||||||
|
</a>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<button class="button is-medium is-primary is-inverted is-outlined ">
|
||||||
|
<a th:href="@{~/space/__${space}__/object-form}">
|
||||||
|
<span class="icon is-small">
|
||||||
|
<i class="fas fa-upload"></i>
|
||||||
|
</span>
|
||||||
|
<span>Upload File</span>
|
||||||
|
</a>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<script th:replace="fragments/notification :: script"/>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
41
aws/s3/src/main/resources/templates/space-form.html
Normal file
41
aws/s3/src/main/resources/templates/space-form.html
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html xmlns="http://www.w3.org/1999/xhtml"
|
||||||
|
xmlns:th="http://www.thymeleaf.org">
|
||||||
|
<head th:replace="fragments/head :: head"/>
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<div th:replace="fragments/navbar :: navbar"/>
|
||||||
|
|
||||||
|
<section class="hero is-fullheight-with-navbar is-primary is-bold">
|
||||||
|
|
||||||
|
<section class="hero-body columns is-centered m-0">
|
||||||
|
<div class="column is-half ">
|
||||||
|
<form action="#" th:action="@{/space-form}" th:object="${space}" method="post">
|
||||||
|
<div class="field">
|
||||||
|
<div class="control has-icons-left">
|
||||||
|
<input class="input is-medium" type="text" th:field="*{name}" placeholder="Name of your space">
|
||||||
|
<span class="icon is-small is-left">
|
||||||
|
<i class="fas fa-heading"></i>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<p class="help">Human readable name of the bucket</p>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<div class="field is-grouped is-grouped-centered">
|
||||||
|
<p class="control">
|
||||||
|
<button class="button is-primary is-outlined is-inverted" type="submit">
|
||||||
|
Submit
|
||||||
|
</button>
|
||||||
|
</p>
|
||||||
|
<p class="control">
|
||||||
|
<a class="button is-primary is-outlined is-inverted" href="space">
|
||||||
|
Cancel
|
||||||
|
</a>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</section>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
73
aws/s3/src/main/resources/templates/space-overview.html
Normal file
73
aws/s3/src/main/resources/templates/space-overview.html
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html xmlns="http://www.w3.org/1999/xhtml"
|
||||||
|
xmlns:th="http://www.thymeleaf.org">
|
||||||
|
<head th:replace="fragments/head :: head"/>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<div th:replace="fragments/navbar :: navbar"/>
|
||||||
|
|
||||||
|
<section class="hero is-fullheight-with-navbar is-primary is-bold">
|
||||||
|
|
||||||
|
<section class="hero-body columns is-centered m-0">
|
||||||
|
<div class="column is-half has-text-centered">
|
||||||
|
<div th:replace="fragments/notification :: notification"/>
|
||||||
|
<div th:each="space: ${spaces}" class="card m-4">
|
||||||
|
<div class="card-content">
|
||||||
|
<div class="media">
|
||||||
|
<div class="media-content">
|
||||||
|
<p class="title is-4 has-text-dark" th:text="${space.name}"/>
|
||||||
|
<p class="subtitle is-6 has-text-grey-light mb-1" th:text="${space.bucket}"/>
|
||||||
|
<p th:if=${space.ttl} class="subtitle is-6 has-text-grey-light"
|
||||||
|
th:text="${'Only storing files for ' + space.ttl + ' days'}"/>
|
||||||
|
<p th:unless=${space.ttl} class="subtitle is-6 has-text-grey-light"
|
||||||
|
>Permanent Storage</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="card-footer">
|
||||||
|
<a th:href="@{space/__${space.name}__}"
|
||||||
|
class="card-footer-item has-text-info">
|
||||||
|
<span class="icon is-small pr-3">
|
||||||
|
<i class="fas fa-info"></i>
|
||||||
|
</span>
|
||||||
|
<span>Details</span>
|
||||||
|
</a>
|
||||||
|
<a th:unless="${space.ttl}" th:href="@{space/temporary/__${space.name}__}"
|
||||||
|
class="card-footer-item has-text-info">
|
||||||
|
<span class="icon is-small pr-3">
|
||||||
|
<i class="fas fa-hourglass-half"></i>
|
||||||
|
</span>
|
||||||
|
<span>Make Temporary</span>
|
||||||
|
</a>
|
||||||
|
<a th:if="${space.ttl}" th:href="@{space/permanent/__${space.name}__}"
|
||||||
|
class="card-footer-item has-text-info">
|
||||||
|
<span class="icon is-small pr-3">
|
||||||
|
<i class="fas fa-infinity"></i>
|
||||||
|
</span>
|
||||||
|
<span>Make Permanent</span>
|
||||||
|
</a>
|
||||||
|
<a th:href="@{space/delete/__${space.name}__}"
|
||||||
|
class="card-footer-item has-text-danger">
|
||||||
|
<span class="icon is-small pr-3">
|
||||||
|
<i class="fas fa-trash-alt"></i>
|
||||||
|
</span>
|
||||||
|
<span>Delete</span>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<button class="button is-medium is-primary is-inverted is-outlined ">
|
||||||
|
<a href="space-form">
|
||||||
|
<span class="icon is-small">
|
||||||
|
<i class="fas fa-plus-square"></i>
|
||||||
|
</span>
|
||||||
|
<span>New Space</span>
|
||||||
|
</a>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<script th:replace="fragments/notification :: script"/>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
13
aws/s3/src/test/java/io/jgoerner/s3/S3ApplicationTests.java
Normal file
13
aws/s3/src/test/java/io/jgoerner/s3/S3ApplicationTests.java
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
package io.jgoerner.s3;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.springframework.boot.test.context.SpringBootTest;
|
||||||
|
|
||||||
|
@SpringBootTest
|
||||||
|
class S3ApplicationTests {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void contextLoads() {
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
5
aws/spring-cloud-caching-redis/Dockerfile
Normal file
5
aws/spring-cloud-caching-redis/Dockerfile
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
FROM --platform=linux/amd64 adoptopenjdk/openjdk11:jre-11.0.10_9-alpine
|
||||||
|
ARG JAR_FILE=build/libs/*.jar
|
||||||
|
COPY ${JAR_FILE} service.jar
|
||||||
|
EXPOSE 8080
|
||||||
|
ENTRYPOINT [ "sh", "-c", "java -jar -XX:+UseContainerSupport -XX:MaxRAMPercentage=75.0 service.jar" ]
|
||||||
4
aws/spring-cloud-caching-redis/README.md
Normal file
4
aws/spring-cloud-caching-redis/README.md
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
# Caching In Spring Boot Application with ElastiCache for Redis
|
||||||
|
|
||||||
|
* This example showcases how you can configure spring cache and connect it with
|
||||||
|
AWS ElastiCache
|
||||||
44
aws/spring-cloud-caching-redis/build.gradle
Normal file
44
aws/spring-cloud-caching-redis/build.gradle
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
plugins {
|
||||||
|
id 'org.springframework.boot' version '2.4.5'
|
||||||
|
id 'io.spring.dependency-management' version '1.0.11.RELEASE'
|
||||||
|
id 'java'
|
||||||
|
}
|
||||||
|
|
||||||
|
group = 'io.reflectoring'
|
||||||
|
version = '0.0.1-SNAPSHOT'
|
||||||
|
sourceCompatibility = '11'
|
||||||
|
|
||||||
|
configurations {
|
||||||
|
compileOnly {
|
||||||
|
extendsFrom annotationProcessor
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
repositories {
|
||||||
|
mavenCentral()
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
|
||||||
|
implementation 'org.springframework.boot:spring-boot-starter-validation'
|
||||||
|
implementation 'org.springframework.boot:spring-boot-starter-web'
|
||||||
|
implementation 'org.springframework.boot:spring-boot-starter-cache'
|
||||||
|
implementation 'org.springframework.boot:spring-boot-starter-data-redis'
|
||||||
|
implementation 'io.awspring.cloud:spring-cloud-starter-aws'
|
||||||
|
// implementation 'com.amazonaws:aws-java-sdk-cloudformation'
|
||||||
|
implementation 'com.amazonaws:aws-java-sdk-elasticache'
|
||||||
|
compileOnly 'org.projectlombok:lombok'
|
||||||
|
runtimeOnly 'com.h2database:h2'
|
||||||
|
annotationProcessor 'org.projectlombok:lombok'
|
||||||
|
testImplementation 'org.springframework.boot:spring-boot-starter-test'
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencyManagement {
|
||||||
|
imports {
|
||||||
|
mavenBom 'io.awspring.cloud:spring-cloud-aws-dependencies:2.3.1'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
test {
|
||||||
|
useJUnitPlatform()
|
||||||
|
}
|
||||||
BIN
aws/spring-cloud-caching-redis/gradle/wrapper/gradle-wrapper.jar
vendored
Normal file
BIN
aws/spring-cloud-caching-redis/gradle/wrapper/gradle-wrapper.jar
vendored
Normal file
Binary file not shown.
5
aws/spring-cloud-caching-redis/gradle/wrapper/gradle-wrapper.properties
vendored
Normal file
5
aws/spring-cloud-caching-redis/gradle/wrapper/gradle-wrapper.properties
vendored
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
distributionBase=GRADLE_USER_HOME
|
||||||
|
distributionPath=wrapper/dists
|
||||||
|
distributionUrl=https\://services.gradle.org/distributions/gradle-6.8.3-bin.zip
|
||||||
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
|
zipStorePath=wrapper/dists
|
||||||
185
aws/spring-cloud-caching-redis/gradlew
vendored
Executable file
185
aws/spring-cloud-caching-redis/gradlew
vendored
Executable file
@@ -0,0 +1,185 @@
|
|||||||
|
#!/usr/bin/env sh
|
||||||
|
|
||||||
|
#
|
||||||
|
# Copyright 2015 the original author or authors.
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
# you may not use this file except in compliance with the License.
|
||||||
|
# You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
#
|
||||||
|
|
||||||
|
##############################################################################
|
||||||
|
##
|
||||||
|
## Gradle start up script for UN*X
|
||||||
|
##
|
||||||
|
##############################################################################
|
||||||
|
|
||||||
|
# Attempt to set APP_HOME
|
||||||
|
# Resolve links: $0 may be a link
|
||||||
|
PRG="$0"
|
||||||
|
# Need this for relative symlinks.
|
||||||
|
while [ -h "$PRG" ] ; do
|
||||||
|
ls=`ls -ld "$PRG"`
|
||||||
|
link=`expr "$ls" : '.*-> \(.*\)$'`
|
||||||
|
if expr "$link" : '/.*' > /dev/null; then
|
||||||
|
PRG="$link"
|
||||||
|
else
|
||||||
|
PRG=`dirname "$PRG"`"/$link"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
SAVED="`pwd`"
|
||||||
|
cd "`dirname \"$PRG\"`/" >/dev/null
|
||||||
|
APP_HOME="`pwd -P`"
|
||||||
|
cd "$SAVED" >/dev/null
|
||||||
|
|
||||||
|
APP_NAME="Gradle"
|
||||||
|
APP_BASE_NAME=`basename "$0"`
|
||||||
|
|
||||||
|
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||||
|
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
||||||
|
|
||||||
|
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||||
|
MAX_FD="maximum"
|
||||||
|
|
||||||
|
warn () {
|
||||||
|
echo "$*"
|
||||||
|
}
|
||||||
|
|
||||||
|
die () {
|
||||||
|
echo
|
||||||
|
echo "$*"
|
||||||
|
echo
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# OS specific support (must be 'true' or 'false').
|
||||||
|
cygwin=false
|
||||||
|
msys=false
|
||||||
|
darwin=false
|
||||||
|
nonstop=false
|
||||||
|
case "`uname`" in
|
||||||
|
CYGWIN* )
|
||||||
|
cygwin=true
|
||||||
|
;;
|
||||||
|
Darwin* )
|
||||||
|
darwin=true
|
||||||
|
;;
|
||||||
|
MINGW* )
|
||||||
|
msys=true
|
||||||
|
;;
|
||||||
|
NONSTOP* )
|
||||||
|
nonstop=true
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
|
||||||
|
|
||||||
|
|
||||||
|
# Determine the Java command to use to start the JVM.
|
||||||
|
if [ -n "$JAVA_HOME" ] ; then
|
||||||
|
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
|
||||||
|
# IBM's JDK on AIX uses strange locations for the executables
|
||||||
|
JAVACMD="$JAVA_HOME/jre/sh/java"
|
||||||
|
else
|
||||||
|
JAVACMD="$JAVA_HOME/bin/java"
|
||||||
|
fi
|
||||||
|
if [ ! -x "$JAVACMD" ] ; then
|
||||||
|
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
|
||||||
|
|
||||||
|
Please set the JAVA_HOME variable in your environment to match the
|
||||||
|
location of your Java installation."
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
JAVACMD="java"
|
||||||
|
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||||
|
|
||||||
|
Please set the JAVA_HOME variable in your environment to match the
|
||||||
|
location of your Java installation."
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Increase the maximum file descriptors if we can.
|
||||||
|
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
|
||||||
|
MAX_FD_LIMIT=`ulimit -H -n`
|
||||||
|
if [ $? -eq 0 ] ; then
|
||||||
|
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
|
||||||
|
MAX_FD="$MAX_FD_LIMIT"
|
||||||
|
fi
|
||||||
|
ulimit -n $MAX_FD
|
||||||
|
if [ $? -ne 0 ] ; then
|
||||||
|
warn "Could not set maximum file descriptor limit: $MAX_FD"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# For Darwin, add options to specify how the application appears in the dock
|
||||||
|
if $darwin; then
|
||||||
|
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
|
||||||
|
fi
|
||||||
|
|
||||||
|
# For Cygwin or MSYS, switch paths to Windows format before running java
|
||||||
|
if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
|
||||||
|
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
|
||||||
|
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
|
||||||
|
|
||||||
|
JAVACMD=`cygpath --unix "$JAVACMD"`
|
||||||
|
|
||||||
|
# We build the pattern for arguments to be converted via cygpath
|
||||||
|
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
|
||||||
|
SEP=""
|
||||||
|
for dir in $ROOTDIRSRAW ; do
|
||||||
|
ROOTDIRS="$ROOTDIRS$SEP$dir"
|
||||||
|
SEP="|"
|
||||||
|
done
|
||||||
|
OURCYGPATTERN="(^($ROOTDIRS))"
|
||||||
|
# Add a user-defined pattern to the cygpath arguments
|
||||||
|
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
|
||||||
|
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
|
||||||
|
fi
|
||||||
|
# Now convert the arguments - kludge to limit ourselves to /bin/sh
|
||||||
|
i=0
|
||||||
|
for arg in "$@" ; do
|
||||||
|
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
|
||||||
|
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
|
||||||
|
|
||||||
|
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
|
||||||
|
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
|
||||||
|
else
|
||||||
|
eval `echo args$i`="\"$arg\""
|
||||||
|
fi
|
||||||
|
i=`expr $i + 1`
|
||||||
|
done
|
||||||
|
case $i in
|
||||||
|
0) set -- ;;
|
||||||
|
1) set -- "$args0" ;;
|
||||||
|
2) set -- "$args0" "$args1" ;;
|
||||||
|
3) set -- "$args0" "$args1" "$args2" ;;
|
||||||
|
4) set -- "$args0" "$args1" "$args2" "$args3" ;;
|
||||||
|
5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
|
||||||
|
6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
|
||||||
|
7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
|
||||||
|
8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
|
||||||
|
9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
|
||||||
|
esac
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Escape application args
|
||||||
|
save () {
|
||||||
|
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
|
||||||
|
echo " "
|
||||||
|
}
|
||||||
|
APP_ARGS=`save "$@"`
|
||||||
|
|
||||||
|
# Collect all arguments for the java command, following the shell quoting and substitution rules
|
||||||
|
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
|
||||||
|
|
||||||
|
exec "$JAVACMD" "$@"
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user