Compare commits
166 Commits
dependabot
...
feature-fl
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c347841a3c | ||
|
|
d715f36b80 | ||
|
|
cf86383325 | ||
|
|
a09f5e7aa0 | ||
|
|
b72173ad27 | ||
|
|
aac07331e8 | ||
|
|
45590a81c2 | ||
|
|
1e39c49cca | ||
|
|
c8bd0660f5 | ||
|
|
2874daed69 | ||
|
|
35a820acbb | ||
|
|
83e5e1048e | ||
|
|
28147ad44c | ||
|
|
154453808d | ||
|
|
b7139e9073 | ||
|
|
64499f5582 | ||
|
|
c870f5dd4c | ||
|
|
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 |
2
.github/workflows/ci.yml
vendored
2
.github/workflows/ci.yml
vendored
@@ -17,7 +17,7 @@ jobs:
|
||||
matrix:
|
||||
# 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.
|
||||
module: [ "module1", "module2", "module3", "module4", "module5" ]
|
||||
module: [ "module1", "module2", "module3", "module4", "module5", "module6" ]
|
||||
|
||||
steps:
|
||||
|
||||
|
||||
2
.gitignore
vendored
2
.gitignore
vendored
@@ -1,3 +1,5 @@
|
||||
**/.idea/
|
||||
**/*.iml
|
||||
**/.DS_Store
|
||||
**/.terraform
|
||||
|
||||
|
||||
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();
|
||||
|
||||
}
|
||||
}
|
||||
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" "$@"
|
||||
89
aws/spring-cloud-caching-redis/gradlew.bat
vendored
Normal file
89
aws/spring-cloud-caching-redis/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
|
||||
1
aws/spring-cloud-caching-redis/settings.gradle
Normal file
1
aws/spring-cloud-caching-redis/settings.gradle
Normal file
@@ -0,0 +1 @@
|
||||
rootProject.name = 'spring-cloud-redis'
|
||||
@@ -0,0 +1,12 @@
|
||||
package io.reflectoring.springcloudredis;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
|
||||
@SpringBootApplication
|
||||
public class SpringCloudRedisApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(SpringCloudRedisApplication.class, args);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
package io.reflectoring.springcloudredis.configuration;
|
||||
|
||||
import org.springframework.cache.annotation.EnableCaching;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
@Configuration
|
||||
@EnableCaching
|
||||
public class EnableCache {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
package io.reflectoring.springcloudredis.controller;
|
||||
|
||||
import io.reflectoring.springcloudredis.entity.Product;
|
||||
import io.reflectoring.springcloudredis.model.ProductInput;
|
||||
import io.reflectoring.springcloudredis.service.ProductService;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import javax.validation.Valid;
|
||||
import java.util.List;
|
||||
|
||||
@Slf4j(topic = "PRODUCT_CONTROLLER")
|
||||
@RestController
|
||||
@RequestMapping("/product")
|
||||
@AllArgsConstructor
|
||||
public class ProductController {
|
||||
private final ProductService productService;
|
||||
|
||||
@GetMapping("/{id}")
|
||||
public Product getProduct(@PathVariable String id){
|
||||
return productService.getProduct(id);
|
||||
}
|
||||
|
||||
@DeleteMapping("/{id}")
|
||||
public void deleteProduct(@PathVariable String id){
|
||||
productService.deleteProduct(id);
|
||||
}
|
||||
|
||||
@PutMapping("/{id}")
|
||||
public Product updateProduct(@PathVariable String id, @RequestBody @Valid ProductInput input){
|
||||
return productService.updateProduct(id, input);
|
||||
}
|
||||
|
||||
@PostMapping
|
||||
public Product addProduct(@RequestBody @Valid ProductInput input){
|
||||
return productService.addProduct(input);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user