Created
May 4, 2019 16:56
-
-
Save InukVT/1f9c5fbef77609e01cb57353d7acfc4e to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import Vapor | |
| import Service | |
| import Crypto | |
| import Foundation | |
| private var b2CacheKey = "__b2_authorization_token" | |
| public struct B2Config: Service { | |
| public let keyID: String | |
| public let applicationID: String | |
| public let bucketID: String | |
| public init(keyID: String, applicationID: String, bucketID: String) { | |
| self.applicationID = applicationID | |
| self.keyID = keyID | |
| self.bucketID = bucketID | |
| } | |
| } | |
| public final class B2: ServiceType { | |
| private let config: B2Config | |
| private let client: Client | |
| private let cache: KeyedCache | |
| private init(config: B2Config, client: Client, cache: KeyedCache) { | |
| self.config = config | |
| self.client = client | |
| self.cache = cache | |
| } | |
| public static func makeService(for worker: Container) throws -> B2 { | |
| return B2( | |
| config: try worker.make(), | |
| client: try worker.client(), | |
| cache: try worker.make() | |
| ) | |
| } | |
| public func upload(file: File) throws -> Future<Response> { | |
| return authorize() | |
| .flatMap(self.getUploadUrl) | |
| .flatMap { uploadUrl in | |
| let uploadAuthorizationToken = uploadUrl.authorizationToken | |
| let fileName = file.filename | |
| let sha1 = try SHA1.hash(file.data).hexEncodedString() | |
| var b2Headers: [String: String] = ["Authorization": uploadAuthorizationToken] | |
| b2Headers["X-Bz-File-Name"] = fileName | |
| b2Headers["Content-Type"] = file.contentType?.description ?? "b2/X-auto" | |
| b2Headers["Content-Length"] = "\(file.data.count + 40)" | |
| b2Headers["X-Bz-Content-Sha1"] = sha1 | |
| let headers = HTTPHeaders(b2Headers.map { $0 }) | |
| let request = Request(using: self.client.container) | |
| request.http.method = .POST | |
| request.http.headers = headers | |
| request.http.body = file.data.convertToHTTPBody() | |
| request.http.url = URL(string: uploadUrl.uploadUrl)! | |
| return self.client.send(request) | |
| } | |
| } | |
| private func authorize() -> Future<B2Auth> { | |
| return cache.get(b2CacheKey, as: B2Auth.self) | |
| .flatMap { auth in | |
| if let auth = auth { | |
| return self.client.container.future(auth) | |
| } | |
| let keyID = self.config.keyID | |
| let applicationID = self.config.applicationID | |
| let auth = ((keyID + ":" + applicationID).data(using: .utf8) ?? Data()).base64EncodedString() | |
| let request = Request(using: self.client.container) | |
| request.http.method = .GET | |
| request.http.headers.add(name: "Authorization", value: "Basic \(auth)") | |
| request.http.url = URL(string: "https://api.backblazeb2.com/b2api/v2/b2_authorize_account")! | |
| return self.client.send(request) | |
| .flatMap { try $0.content.decode(B2Auth.self) } | |
| .flatMap { self.cache.set(b2CacheKey, to: $0).transform(to: $0) } | |
| } | |
| } | |
| private func getUploadUrl(auth: B2Auth) throws -> Future<B2UploadUrl> { | |
| let request = Request(using: client.container) | |
| request.http.method = .POST | |
| request.http.headers.add(name: "Authorization", value: auth.authorizationToken) | |
| try request.content.encode(json: B2UploadUrlRequest(bucketId: config.bucketID)) | |
| request.http.url = URL(string: auth.apiUrl + "/b2api/v2/b2_get_upload_url")! | |
| return client.send(request) | |
| .flatMap { try $0.content.decode(B2UploadUrl.self) } | |
| } | |
| } | |
| private struct B2Auth: Codable { | |
| let absoluteMinimumPartSize: Int | |
| let accountId: String | |
| let apiUrl: String | |
| let authorizationToken: String | |
| let downloadUrl: String | |
| let recommendedPartSize: Int | |
| } | |
| private struct B2UploadUrl: Codable { | |
| let bucketId: String | |
| let uploadUrl: String | |
| let authorizationToken: String | |
| } | |
| private struct B2UploadUrlRequest: Encodable { | |
| let bucketId: String | |
| } | |
| public extension Container { | |
| public func b2() throws -> B2 { | |
| return try B2.makeService(for: self) | |
| } | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| //import FluentSQLite | |
| import Vapor | |
| import S3 | |
| import ServiceExt | |
| import B2 | |
| /// Called before your application initializes. | |
| public func configure(_ config: inout Config, _ env: inout Environment, _ services: inout Services) throws { | |
| /// Register providers first | |
| //try services.register(FluentSQLiteProvider()) | |
| /// Register routes to the router | |
| let router = EngineRouter.default() | |
| try routes(router) | |
| services.register(router, as: Router.self) | |
| /// Register middleware | |
| var middlewares = MiddlewareConfig() // Create _empty_ middleware config | |
| /// middlewares.use(FileMiddleware.self) // Serves files from `Public/` directory | |
| middlewares.use(ErrorMiddleware.self) // Catches errors and converts to HTTP response | |
| services.register(middlewares) | |
| Environment.dotenv() | |
| if Environment.get("CLOUDPROVIDER") == "aws" { | |
| guard let accessKey: String = Environment.get("AWSACCESSKEY"), let secretKey: String = Environment.get("AWSSECRETKEY") else { | |
| throw Abort(.internalServerError, reason: "AWS environment variables missing") | |
| } | |
| print(accessKey) | |
| let region = Region.euCentral1 | |
| try services.register(s3: S3Signer.Config(accessKey: accessKey, | |
| secretKey: secretKey, | |
| region: region), | |
| defaultBucket: "bucket-inuk") | |
| } | |
| else if Environment.get("CLOUDPROVIDER") == "b2" { | |
| guard let applicationKey: String = Environment.get("B2APPLICATIONKEY"), let keyID: String = Environment.get("B2KEYID") else { | |
| throw Abort(.internalServerError, reason: "B2 environment variables missing") | |
| } | |
| let B2Creds = B2Config(keyID: keyID, | |
| applicationID: applicationKey, | |
| bucketID: "c57dae5326d7b3ac66660118") | |
| services.register(B2Creds) | |
| services.register(B2.self) | |
| } | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import Vapor | |
| import S3 | |
| import B2 | |
| //rimport Foundation | |
| /// Register your application's routes here. | |
| public func routes(_ router: Router) throws { | |
| // Basic "It works" example | |
| router.get { req in | |
| return "It works!" | |
| } | |
| let s3Routes = router.grouped("s3") | |
| func postS3(_ req: Request) throws -> Future<Response> { | |
| return try req.content.decode(Filer.self).flatMap { filer in | |
| let mimeType = filer.file.contentType?.description ?? MediaType.plainText.description | |
| let file = File.Upload(data: filer.file.data, destination: filer.file.filename, access: .publicRead, mime: mimeType) | |
| return try req.makeS3Client().put(file: file, on: req).map(to: Response.self) {_ in | |
| return req.redirect(to: "") | |
| } | |
| } | |
| } | |
| func deletes3(_ req: Request) throws -> Future<HTTPStatus> { | |
| return try req.content.decode(Delete.self).flatMap { response in | |
| return try req.makeS3Client().delete(file: response.file as LocationConvertible, on: req).transform(to: HTTPStatus.noContent) | |
| } | |
| } | |
| s3Routes.post("post", use: postS3) | |
| s3Routes.delete("delete", use: deletes3) | |
| let b2Routes = router.grouped("b2") | |
| func postB2(_ req: Request) throws -> Future<Response> { | |
| return try req.content.decode(Filer.self).flatMap { filer in | |
| let file = filer.file | |
| return try req.make(B2.self).upload(file: file).map {_ in | |
| return req.redirect(to: "") | |
| } | |
| } | |
| } | |
| b2Routes.post("post", use: postB2) | |
| } | |
| struct Filer: Content | |
| { | |
| let file: Core.File | |
| } | |
| struct Delete: Content { | |
| let file: String | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment