From 0ae5a68a9d81547bb9b741458d94b5f1b7374027 Mon Sep 17 00:00:00 2001 From: ivarlovlie Date: Fri, 13 Jan 2023 20:21:20 +0100 Subject: feat: First dev release --- .version | 0 .version-dev | 0 BlobBin/BlobBin.csproj | 25 ----- BlobBin/DB.cs | 42 -------- BlobBin/Dockerfile | 20 ---- .../20230112230354_InitialCreate.Designer.cs | 114 -------------------- BlobBin/Migrations/20230112230354_InitialCreate.cs | 68 ------------ BlobBin/Migrations/DBModelSnapshot.cs | 111 ------------------- BlobBin/Program.cs | 120 --------------------- BlobBin/Properties/launchSettings.json | 15 --- BlobBin/wwwroot/index.html | 80 -------------- build_and_push.sh | 90 ++++++++++++++++ src/BlobBin.csproj | 25 +++++ src/DB.cs | 42 ++++++++ src/Dockerfile | 20 ++++ .../20230112230354_InitialCreate.Designer.cs | 114 ++++++++++++++++++++ src/Migrations/20230112230354_InitialCreate.cs | 68 ++++++++++++ src/Migrations/DBModelSnapshot.cs | 111 +++++++++++++++++++ src/Program.cs | 120 +++++++++++++++++++++ src/Properties/launchSettings.json | 15 +++ src/wwwroot/index.html | 80 ++++++++++++++ 21 files changed, 685 insertions(+), 595 deletions(-) create mode 100644 .version create mode 100644 .version-dev delete mode 100644 BlobBin/BlobBin.csproj delete mode 100644 BlobBin/DB.cs delete mode 100644 BlobBin/Dockerfile delete mode 100644 BlobBin/Migrations/20230112230354_InitialCreate.Designer.cs delete mode 100644 BlobBin/Migrations/20230112230354_InitialCreate.cs delete mode 100644 BlobBin/Migrations/DBModelSnapshot.cs delete mode 100644 BlobBin/Program.cs delete mode 100644 BlobBin/Properties/launchSettings.json delete mode 100644 BlobBin/wwwroot/index.html create mode 100755 build_and_push.sh create mode 100644 src/BlobBin.csproj create mode 100644 src/DB.cs create mode 100644 src/Dockerfile create mode 100644 src/Migrations/20230112230354_InitialCreate.Designer.cs create mode 100644 src/Migrations/20230112230354_InitialCreate.cs create mode 100644 src/Migrations/DBModelSnapshot.cs create mode 100644 src/Program.cs create mode 100644 src/Properties/launchSettings.json create mode 100644 src/wwwroot/index.html diff --git a/.version b/.version new file mode 100644 index 0000000..e69de29 diff --git a/.version-dev b/.version-dev new file mode 100644 index 0000000..e69de29 diff --git a/BlobBin/BlobBin.csproj b/BlobBin/BlobBin.csproj deleted file mode 100644 index 263a517..0000000 --- a/BlobBin/BlobBin.csproj +++ /dev/null @@ -1,25 +0,0 @@ - - - - net7.0 - enable - enable - Linux - - - - - - runtime; build; native; contentfiles; analyzers; buildtransitive - all - - - - - - - .dockerignore - - - - diff --git a/BlobBin/DB.cs b/BlobBin/DB.cs deleted file mode 100644 index a736dc2..0000000 --- a/BlobBin/DB.cs +++ /dev/null @@ -1,42 +0,0 @@ -using Microsoft.EntityFrameworkCore; - -namespace BlobBin; - -public class DB : DbContext -{ - public DB(DbContextOptions options) : base(options) { } - - public DbSet Files { get; set; } - public DbSet Pastes { get; set; } -} - -public class UploadEntityBase -{ - public UploadEntityBase() { - Id = Guid.NewGuid(); - CreatedAt = DateTime.UtcNow; - } - - public Guid Id { get; set; } - public string PublicId { get; set; } - public DateTime CreatedAt { get; set; } - public string CreatedBy { get; set; } - public DateTime? DeletedAt { get; set; } - public string? PasswordHash { get; set; } - public bool Singleton { get; set; } - public string? AutoDeleteAfter { get; set; } - public string? MimeType { get; set; } -} - -public class File : UploadEntityBase -{ - public string? Name { get; set; } - public long Length { get; set; } -} - -public class Paste : UploadEntityBase -{ - public string? Name { get; set; } - public string? Content { get; set; } - public long Length { get; set; } -} \ No newline at end of file diff --git a/BlobBin/Dockerfile b/BlobBin/Dockerfile deleted file mode 100644 index 9b1f38e..0000000 --- a/BlobBin/Dockerfile +++ /dev/null @@ -1,20 +0,0 @@ -FROM mcr.microsoft.com/dotnet/aspnet:7.0 AS base -WORKDIR /app -EXPOSE 80 -EXPOSE 443 - -FROM mcr.microsoft.com/dotnet/sdk:7.0 AS build -WORKDIR /src -COPY ["BlobBin/BlobBin.csproj", "BlobBin/"] -RUN dotnet restore "BlobBin/BlobBin.csproj" -COPY . . -WORKDIR "/src/BlobBin" -RUN dotnet build "BlobBin.csproj" -c Release -o /app/build - -FROM build AS publish -RUN dotnet publish "BlobBin.csproj" -c Release -o /app/publish - -FROM base AS final -WORKDIR /app -COPY --from=publish /app/publish . -ENTRYPOINT ["dotnet", "BlobBin.dll"] diff --git a/BlobBin/Migrations/20230112230354_InitialCreate.Designer.cs b/BlobBin/Migrations/20230112230354_InitialCreate.Designer.cs deleted file mode 100644 index b5a06c3..0000000 --- a/BlobBin/Migrations/20230112230354_InitialCreate.Designer.cs +++ /dev/null @@ -1,114 +0,0 @@ -// -using System; -using BlobBin; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Migrations; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; - -#nullable disable - -namespace BlobBin.Migrations -{ - [DbContext(typeof(DB))] - [Migration("20230112230354_InitialCreate")] - partial class InitialCreate - { - /// - protected override void BuildTargetModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder.HasAnnotation("ProductVersion", "7.0.2"); - - modelBuilder.Entity("BlobBin.File", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("TEXT"); - - b.Property("AutoDeleteAfter") - .HasColumnType("TEXT"); - - b.Property("CreatedAt") - .HasColumnType("TEXT"); - - b.Property("CreatedBy") - .IsRequired() - .HasColumnType("TEXT"); - - b.Property("DeletedAt") - .HasColumnType("TEXT"); - - b.Property("Length") - .HasColumnType("INTEGER"); - - b.Property("MimeType") - .HasColumnType("TEXT"); - - b.Property("Name") - .HasColumnType("TEXT"); - - b.Property("PasswordHash") - .HasColumnType("TEXT"); - - b.Property("PublicId") - .IsRequired() - .HasColumnType("TEXT"); - - b.Property("Singleton") - .HasColumnType("INTEGER"); - - b.HasKey("Id"); - - b.ToTable("Files"); - }); - - modelBuilder.Entity("BlobBin.Paste", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("TEXT"); - - b.Property("AutoDeleteAfter") - .HasColumnType("TEXT"); - - b.Property("Content") - .HasColumnType("TEXT"); - - b.Property("CreatedAt") - .HasColumnType("TEXT"); - - b.Property("CreatedBy") - .IsRequired() - .HasColumnType("TEXT"); - - b.Property("DeletedAt") - .HasColumnType("TEXT"); - - b.Property("Length") - .HasColumnType("INTEGER"); - - b.Property("MimeType") - .HasColumnType("TEXT"); - - b.Property("Name") - .HasColumnType("TEXT"); - - b.Property("PasswordHash") - .HasColumnType("TEXT"); - - b.Property("PublicId") - .IsRequired() - .HasColumnType("TEXT"); - - b.Property("Singleton") - .HasColumnType("INTEGER"); - - b.HasKey("Id"); - - b.ToTable("Pastes"); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/BlobBin/Migrations/20230112230354_InitialCreate.cs b/BlobBin/Migrations/20230112230354_InitialCreate.cs deleted file mode 100644 index 97f9c36..0000000 --- a/BlobBin/Migrations/20230112230354_InitialCreate.cs +++ /dev/null @@ -1,68 +0,0 @@ -using System; -using Microsoft.EntityFrameworkCore.Migrations; - -#nullable disable - -namespace BlobBin.Migrations -{ - /// - public partial class InitialCreate : Migration - { - /// - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.CreateTable( - name: "Files", - columns: table => new - { - Id = table.Column(type: "TEXT", nullable: false), - Name = table.Column(type: "TEXT", nullable: true), - Length = table.Column(type: "INTEGER", nullable: false), - PublicId = table.Column(type: "TEXT", nullable: false), - CreatedAt = table.Column(type: "TEXT", nullable: false), - CreatedBy = table.Column(type: "TEXT", nullable: false), - DeletedAt = table.Column(type: "TEXT", nullable: true), - PasswordHash = table.Column(type: "TEXT", nullable: true), - Singleton = table.Column(type: "INTEGER", nullable: false), - AutoDeleteAfter = table.Column(type: "TEXT", nullable: true), - MimeType = table.Column(type: "TEXT", nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK_Files", x => x.Id); - }); - - migrationBuilder.CreateTable( - name: "Pastes", - columns: table => new - { - Id = table.Column(type: "TEXT", nullable: false), - Name = table.Column(type: "TEXT", nullable: true), - Content = table.Column(type: "TEXT", nullable: true), - Length = table.Column(type: "INTEGER", nullable: false), - PublicId = table.Column(type: "TEXT", nullable: false), - CreatedAt = table.Column(type: "TEXT", nullable: false), - CreatedBy = table.Column(type: "TEXT", nullable: false), - DeletedAt = table.Column(type: "TEXT", nullable: true), - PasswordHash = table.Column(type: "TEXT", nullable: true), - Singleton = table.Column(type: "INTEGER", nullable: false), - AutoDeleteAfter = table.Column(type: "TEXT", nullable: true), - MimeType = table.Column(type: "TEXT", nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK_Pastes", x => x.Id); - }); - } - - /// - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropTable( - name: "Files"); - - migrationBuilder.DropTable( - name: "Pastes"); - } - } -} diff --git a/BlobBin/Migrations/DBModelSnapshot.cs b/BlobBin/Migrations/DBModelSnapshot.cs deleted file mode 100644 index 8c9fb26..0000000 --- a/BlobBin/Migrations/DBModelSnapshot.cs +++ /dev/null @@ -1,111 +0,0 @@ -// -using System; -using BlobBin; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; - -#nullable disable - -namespace BlobBin.Migrations -{ - [DbContext(typeof(DB))] - partial class DBModelSnapshot : ModelSnapshot - { - protected override void BuildModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder.HasAnnotation("ProductVersion", "7.0.2"); - - modelBuilder.Entity("BlobBin.File", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("TEXT"); - - b.Property("AutoDeleteAfter") - .HasColumnType("TEXT"); - - b.Property("CreatedAt") - .HasColumnType("TEXT"); - - b.Property("CreatedBy") - .IsRequired() - .HasColumnType("TEXT"); - - b.Property("DeletedAt") - .HasColumnType("TEXT"); - - b.Property("Length") - .HasColumnType("INTEGER"); - - b.Property("MimeType") - .HasColumnType("TEXT"); - - b.Property("Name") - .HasColumnType("TEXT"); - - b.Property("PasswordHash") - .HasColumnType("TEXT"); - - b.Property("PublicId") - .IsRequired() - .HasColumnType("TEXT"); - - b.Property("Singleton") - .HasColumnType("INTEGER"); - - b.HasKey("Id"); - - b.ToTable("Files"); - }); - - modelBuilder.Entity("BlobBin.Paste", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("TEXT"); - - b.Property("AutoDeleteAfter") - .HasColumnType("TEXT"); - - b.Property("Content") - .HasColumnType("TEXT"); - - b.Property("CreatedAt") - .HasColumnType("TEXT"); - - b.Property("CreatedBy") - .IsRequired() - .HasColumnType("TEXT"); - - b.Property("DeletedAt") - .HasColumnType("TEXT"); - - b.Property("Length") - .HasColumnType("INTEGER"); - - b.Property("MimeType") - .HasColumnType("TEXT"); - - b.Property("Name") - .HasColumnType("TEXT"); - - b.Property("PasswordHash") - .HasColumnType("TEXT"); - - b.Property("PublicId") - .IsRequired() - .HasColumnType("TEXT"); - - b.Property("Singleton") - .HasColumnType("INTEGER"); - - b.HasKey("Id"); - - b.ToTable("Pastes"); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/BlobBin/Program.cs b/BlobBin/Program.cs deleted file mode 100644 index e66cd02..0000000 --- a/BlobBin/Program.cs +++ /dev/null @@ -1,120 +0,0 @@ -global using BlobBin; -using IOL.Helpers; -using Microsoft.EntityFrameworkCore; -using File = BlobBin.File; - -var builder = WebApplication.CreateBuilder(args); -builder.Services.AddDbContext(opt => opt.UseSqlite("data source=main.db")); -var app = builder.Build(); - -app.UseFileServer(); -app.UseStatusCodePages(); -app.MapGet("/upload-link", GetUploadLink); -app.MapPost("/upload/{id}", UploadBig); -app.MapPost("/upload", UploadSimple); -app.MapPost("/text", UploadText); -app.MapGet("/b/{id}", GetBlob); -app.Run(); - -IResult GetUploadLink(HttpContext context, DB db) { - var file = new File { - CreatedBy = context.Request.Headers["X-Forwarded-For"].ToString() - }; - db.Files.Add(file); - db.SaveChanges(); - return Results.Text( - context.Request.GetRequestHost() - + "/upload/" - + file.Id - ); -} - -async Task UploadSimple(HttpContext context, DB db) { - if (!context.Request.Form.Files.Any()) { - return Results.BadRequest("No files was found in request"); - } - - var file = new File { - CreatedBy = context.Request.Headers["X-Forwarded-For"].ToString(), - Singleton = context.Request.Form["singleton"] == "on", - AutoDeleteAfter = context.Request.Form["autoDeleteAfter"], - Length = context.Request.Form.Files[0].Length, - Name = context.Request.Form.Files[0].FileName, - MimeType = context.Request.Form.Files[0].ContentType, - PublicId = GetUnusedBlobId(db) - }; - - if (context.Request.Form["password"].ToString().HasValue()) { - file.PasswordHash = PasswordHelper.HashPassword(context.Request.Form["password"]); - } - - - await using var write = System.IO.File.OpenWrite( - Path.Combine(GetFilesDirectoryPath(), file.Id.ToString()) - ); - await context.Request.Form.Files[0].CopyToAsync(write); - db.Files.Add(file); - db.SaveChanges(); - return Results.Text( - context.Request.GetRequestHost() - + "/b/" - + file.PublicId - ); -} - -IResult UploadBig(HttpContext context, DB db) { - return Results.Ok(); -} - -IResult UploadText(HttpContext context, DB db) { - return Results.Ok(); -} - -async Task GetBlob(string id, DB db) { - var file = db.Files.FirstOrDefault(c => c.PublicId == id.Trim()); - if (file == default) return Results.NotFound(); - var reader = await System.IO.File.ReadAllBytesAsync( - Path.Combine( - GetFilesDirectoryPath(), file.Id.ToString() - ) - ); - return Results.File(reader, file.MimeType, file.Name); -} - -string GetFilesDirectoryPath() { - var filesDirectoryPath = Path.Combine( - Directory.GetCurrentDirectory(), - "AppData", - "files" - ); - Directory.CreateDirectory(filesDirectoryPath); - return filesDirectoryPath; -} - -string GetUnusedBlobId(DB db) { - string id() => RandomString.Generate(5); - var res = id(); - while (db.Files.Any(c => c.PublicId == res)) { - res = id(); - } - - return res; -} - -class BlobBase -{ - public string Password { get; set; } - public bool Singleton { get; set; } - public string AutoDeleteAfter { get; set; } -} - -class PasteRequest : BlobBase -{ - public string Text { get; set; } - public string Mime { get; set; } -} - -class UploadRequest : BlobBase -{ - public IFormFile? File { get; set; } -} \ No newline at end of file diff --git a/BlobBin/Properties/launchSettings.json b/BlobBin/Properties/launchSettings.json deleted file mode 100644 index e19510b..0000000 --- a/BlobBin/Properties/launchSettings.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "$schema": "https://json.schemastore.org/launchsettings.json", - "profiles": { - "http": { - "commandName": "Project", - "dotnetRunMessages": true, - "launchBrowser": false, - "launchUrl": "", - "applicationUrl": "http://localhost:5033", - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - } - } - } -} diff --git a/BlobBin/wwwroot/index.html b/BlobBin/wwwroot/index.html deleted file mode 100644 index fa25951..0000000 --- a/BlobBin/wwwroot/index.html +++ /dev/null @@ -1,80 +0,0 @@ - - - - - - Blobbin - - -

Blobbin

-

This is a web service you can upload files and texts to.

-
-
- Upload a file -
- - - - - - - -
-
-
- Upload some text -
- - - - - - - - - -
-
-
- - \ No newline at end of file diff --git a/build_and_push.sh b/build_and_push.sh new file mode 100755 index 0000000..3ea7e26 --- /dev/null +++ b/build_and_push.sh @@ -0,0 +1,90 @@ +#!/usr/bin/env bash + +set -Eueo pipefail + +CURRENT_DEV_VERSION=$(cat .version-dev) +CURRENT_DEV_VERSION_INT=${CURRENT_DEV_VERSION//[!0-9]/} +CURRENT_VERSION=$(cat .version) +CURRENT_VERSION_INT=${CURRENT_VERSION//[!0-9]/} +if [ ${1-prod} == "dev" ]; then + NEW_VERSION="v$((CURRENT_DEV_VERSION_INT + 1))-dev" + OLD_VERSION=$CURRENT_DEV_VERSION +else + NEW_VERSION="v$((CURRENT_VERSION_INT + 1))" + OLD_VERSION=$CURRENT_VERSION +fi +IMAGE_NAME="blob-bin/server" +HUB_NAME="dr.ivar.systems/blob-in/server" + +# Check for uncommited changes and optionally commit them +if [ "$(git status --untracked-files=no --porcelain)" ]; then + echo "Unclean git tree! press CTRL+C to exit or press ENTER to commit and push to the default branch" + read -n 1 + + read -p "Enter commit message: " COMMIT_MESSAGE + git add . + git commit --quiet -m "$COMMIT_MESSAGE" +fi + +if [ ${1-prod} == "dev" ]; then + echo $NEW_VERSION >|.version-dev + git add .version-dev +else + echo $NEW_VERSION >|.version + git add .version +fi + +echo "Starting build of $HUB_NAME:$NEW_VERSION at $(date -u)..." +echo + +# Put version.txt inside of server +pushd src/wwwroot +echo "$NEW_VERSION" >version.txt +git add version.txt +popd + +git commit --quiet -m "chore(release): Bump version" + +read -p "Do you want to tag this build? (y/n) " -n 1 -r +echo +if [[ $REPLY =~ ^[Yy]$ ]]; then + read -p "Enter tag message (can be empty): " TAG_MESSAGE + git tag -am "$TAG_MESSAGE" $NEW_VERSION +fi + +read -p "Do you want to push the latest commits and tags to origin? (y/n) " -n 1 -r +echo +if [[ $REPLY =~ ^[Yy]$ ]]; then + echo "Pushing latest changes to remotes..." + echo + git push --quiet --follow-tags +fi + +# Build docker image +echo "Building docker image" +echo +pushd src +docker build -t $IMAGE_NAME:$NEW_VERSION . + +docker tag $IMAGE_NAME:$NEW_VERSION $HUB_NAME:$NEW_VERSION + +if [ ${1-prod} == "dev" ]; then + docker tag $IMAGE_NAME:$NEW_VERSION $HUB_NAME:latest-dev +fi +if [ ${1-prod} == "prod" ]; then + docker tag $IMAGE_NAME:$NEW_VERSION $HUB_NAME:latest +fi + +# Optionally push images to docker registry +echo "Press CTRL+C to exit or press ENTER to push docker image to registry" +read -n 1 +docker push $HUB_NAME:$NEW_VERSION + +if [ ${1-prod} == "dev" ]; then + docker push $HUB_NAME:latest-dev +fi + +if [ ${1-prod} == "prod" ]; then + docker push $HUB_NAME:latest +fi +popd diff --git a/src/BlobBin.csproj b/src/BlobBin.csproj new file mode 100644 index 0000000..263a517 --- /dev/null +++ b/src/BlobBin.csproj @@ -0,0 +1,25 @@ + + + + net7.0 + enable + enable + Linux + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + + + + .dockerignore + + + + diff --git a/src/DB.cs b/src/DB.cs new file mode 100644 index 0000000..a736dc2 --- /dev/null +++ b/src/DB.cs @@ -0,0 +1,42 @@ +using Microsoft.EntityFrameworkCore; + +namespace BlobBin; + +public class DB : DbContext +{ + public DB(DbContextOptions options) : base(options) { } + + public DbSet Files { get; set; } + public DbSet Pastes { get; set; } +} + +public class UploadEntityBase +{ + public UploadEntityBase() { + Id = Guid.NewGuid(); + CreatedAt = DateTime.UtcNow; + } + + public Guid Id { get; set; } + public string PublicId { get; set; } + public DateTime CreatedAt { get; set; } + public string CreatedBy { get; set; } + public DateTime? DeletedAt { get; set; } + public string? PasswordHash { get; set; } + public bool Singleton { get; set; } + public string? AutoDeleteAfter { get; set; } + public string? MimeType { get; set; } +} + +public class File : UploadEntityBase +{ + public string? Name { get; set; } + public long Length { get; set; } +} + +public class Paste : UploadEntityBase +{ + public string? Name { get; set; } + public string? Content { get; set; } + public long Length { get; set; } +} \ No newline at end of file diff --git a/src/Dockerfile b/src/Dockerfile new file mode 100644 index 0000000..9b1f38e --- /dev/null +++ b/src/Dockerfile @@ -0,0 +1,20 @@ +FROM mcr.microsoft.com/dotnet/aspnet:7.0 AS base +WORKDIR /app +EXPOSE 80 +EXPOSE 443 + +FROM mcr.microsoft.com/dotnet/sdk:7.0 AS build +WORKDIR /src +COPY ["BlobBin/BlobBin.csproj", "BlobBin/"] +RUN dotnet restore "BlobBin/BlobBin.csproj" +COPY . . +WORKDIR "/src/BlobBin" +RUN dotnet build "BlobBin.csproj" -c Release -o /app/build + +FROM build AS publish +RUN dotnet publish "BlobBin.csproj" -c Release -o /app/publish + +FROM base AS final +WORKDIR /app +COPY --from=publish /app/publish . +ENTRYPOINT ["dotnet", "BlobBin.dll"] diff --git a/src/Migrations/20230112230354_InitialCreate.Designer.cs b/src/Migrations/20230112230354_InitialCreate.Designer.cs new file mode 100644 index 0000000..b5a06c3 --- /dev/null +++ b/src/Migrations/20230112230354_InitialCreate.Designer.cs @@ -0,0 +1,114 @@ +// +using System; +using BlobBin; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +#nullable disable + +namespace BlobBin.Migrations +{ + [DbContext(typeof(DB))] + [Migration("20230112230354_InitialCreate")] + partial class InitialCreate + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder.HasAnnotation("ProductVersion", "7.0.2"); + + modelBuilder.Entity("BlobBin.File", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("AutoDeleteAfter") + .HasColumnType("TEXT"); + + b.Property("CreatedAt") + .HasColumnType("TEXT"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("DeletedAt") + .HasColumnType("TEXT"); + + b.Property("Length") + .HasColumnType("INTEGER"); + + b.Property("MimeType") + .HasColumnType("TEXT"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.Property("PasswordHash") + .HasColumnType("TEXT"); + + b.Property("PublicId") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Singleton") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.ToTable("Files"); + }); + + modelBuilder.Entity("BlobBin.Paste", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("AutoDeleteAfter") + .HasColumnType("TEXT"); + + b.Property("Content") + .HasColumnType("TEXT"); + + b.Property("CreatedAt") + .HasColumnType("TEXT"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("DeletedAt") + .HasColumnType("TEXT"); + + b.Property("Length") + .HasColumnType("INTEGER"); + + b.Property("MimeType") + .HasColumnType("TEXT"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.Property("PasswordHash") + .HasColumnType("TEXT"); + + b.Property("PublicId") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Singleton") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.ToTable("Pastes"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/src/Migrations/20230112230354_InitialCreate.cs b/src/Migrations/20230112230354_InitialCreate.cs new file mode 100644 index 0000000..97f9c36 --- /dev/null +++ b/src/Migrations/20230112230354_InitialCreate.cs @@ -0,0 +1,68 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace BlobBin.Migrations +{ + /// + public partial class InitialCreate : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "Files", + columns: table => new + { + Id = table.Column(type: "TEXT", nullable: false), + Name = table.Column(type: "TEXT", nullable: true), + Length = table.Column(type: "INTEGER", nullable: false), + PublicId = table.Column(type: "TEXT", nullable: false), + CreatedAt = table.Column(type: "TEXT", nullable: false), + CreatedBy = table.Column(type: "TEXT", nullable: false), + DeletedAt = table.Column(type: "TEXT", nullable: true), + PasswordHash = table.Column(type: "TEXT", nullable: true), + Singleton = table.Column(type: "INTEGER", nullable: false), + AutoDeleteAfter = table.Column(type: "TEXT", nullable: true), + MimeType = table.Column(type: "TEXT", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_Files", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "Pastes", + columns: table => new + { + Id = table.Column(type: "TEXT", nullable: false), + Name = table.Column(type: "TEXT", nullable: true), + Content = table.Column(type: "TEXT", nullable: true), + Length = table.Column(type: "INTEGER", nullable: false), + PublicId = table.Column(type: "TEXT", nullable: false), + CreatedAt = table.Column(type: "TEXT", nullable: false), + CreatedBy = table.Column(type: "TEXT", nullable: false), + DeletedAt = table.Column(type: "TEXT", nullable: true), + PasswordHash = table.Column(type: "TEXT", nullable: true), + Singleton = table.Column(type: "INTEGER", nullable: false), + AutoDeleteAfter = table.Column(type: "TEXT", nullable: true), + MimeType = table.Column(type: "TEXT", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_Pastes", x => x.Id); + }); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "Files"); + + migrationBuilder.DropTable( + name: "Pastes"); + } + } +} diff --git a/src/Migrations/DBModelSnapshot.cs b/src/Migrations/DBModelSnapshot.cs new file mode 100644 index 0000000..8c9fb26 --- /dev/null +++ b/src/Migrations/DBModelSnapshot.cs @@ -0,0 +1,111 @@ +// +using System; +using BlobBin; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +#nullable disable + +namespace BlobBin.Migrations +{ + [DbContext(typeof(DB))] + partial class DBModelSnapshot : ModelSnapshot + { + protected override void BuildModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder.HasAnnotation("ProductVersion", "7.0.2"); + + modelBuilder.Entity("BlobBin.File", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("AutoDeleteAfter") + .HasColumnType("TEXT"); + + b.Property("CreatedAt") + .HasColumnType("TEXT"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("DeletedAt") + .HasColumnType("TEXT"); + + b.Property("Length") + .HasColumnType("INTEGER"); + + b.Property("MimeType") + .HasColumnType("TEXT"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.Property("PasswordHash") + .HasColumnType("TEXT"); + + b.Property("PublicId") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Singleton") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.ToTable("Files"); + }); + + modelBuilder.Entity("BlobBin.Paste", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("AutoDeleteAfter") + .HasColumnType("TEXT"); + + b.Property("Content") + .HasColumnType("TEXT"); + + b.Property("CreatedAt") + .HasColumnType("TEXT"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("DeletedAt") + .HasColumnType("TEXT"); + + b.Property("Length") + .HasColumnType("INTEGER"); + + b.Property("MimeType") + .HasColumnType("TEXT"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.Property("PasswordHash") + .HasColumnType("TEXT"); + + b.Property("PublicId") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Singleton") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.ToTable("Pastes"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/src/Program.cs b/src/Program.cs new file mode 100644 index 0000000..01ca76b --- /dev/null +++ b/src/Program.cs @@ -0,0 +1,120 @@ +global using BlobBin; +using IOL.Helpers; +using Microsoft.EntityFrameworkCore; +using File = BlobBin.File; + +var builder = WebApplication.CreateBuilder(args); +builder.Services.AddDbContext(opt => opt.UseSqlite("data source=main.db")); +var app = builder.Build(); + +app.UseFileServer(); +app.UseStatusCodePages(); +app.MapGet("/upload-link", GetUploadLink); +app.MapPost("/upload/{id}", UploadBig); +app.MapPost("/upload", UploadSimple); +app.MapPost("/text", UploadText); +app.MapGet("/b/{id}", GetBlob); +app.Run(); + +IResult GetUploadLink(HttpContext context, DB db) { + var file = new File { + CreatedBy = context.Request.Headers["X-Forwarded-For"].ToString() + }; + db.Files.Add(file); + db.SaveChanges(); + return Results.Text( + context.Request.GetRequestHost() + + "/upload/" + + file.Id + ); +} + +async Task UploadSimple(HttpContext context, DB db) { + if (!context.Request.Form.Files.Any()) { + return Results.BadRequest("No files was found in request"); + } + + var file = new File { + CreatedBy = context.Request.Headers["X-Forwarded-For"].ToString(), + Singleton = context.Request.Form["singleton"] == "on", + AutoDeleteAfter = context.Request.Form["autoDeleteAfter"], + Length = context.Request.Form.Files[0].Length, + Name = context.Request.Form.Files[0].FileName, + MimeType = context.Request.Form.Files[0].ContentType, + PublicId = GetUnusedBlobId(db) + }; + + if (context.Request.Form["password"].ToString().HasValue()) { + file.PasswordHash = PasswordHelper.HashPassword(context.Request.Form["password"]); + } + + + await using var write = System.IO.File.OpenWrite( + Path.Combine(GetFilesDirectoryPath(), file.Id.ToString()) + ); + await context.Request.Form.Files[0].CopyToAsync(write); + db.Files.Add(file); + db.SaveChanges(); + return Results.Text( + context.Request.GetRequestHost() + + "/b/" + + file.PublicId + ); +} + +IResult UploadBig(HttpContext context, DB db) { + return Results.Ok(); +} + +IResult UploadText(HttpContext context, DB db) { + return Results.Ok(); +} + +async Task GetBlob(string id, DB db) { + var file = db.Files.FirstOrDefault(c => c.PublicId == id.Trim()); + if (file == default) return Results.NotFound(); + var reader = await System.IO.File.ReadAllBytesAsync( + Path.Combine( + GetFilesDirectoryPath(), file.Id.ToString() + ) + ); + return Results.File(reader, file.MimeType, file.Name); +} + +string GetFilesDirectoryPath() { + var filesDirectoryPath = Path.Combine( + Directory.GetCurrentDirectory(), + "AppData", + "files" + ); + Directory.CreateDirectory(filesDirectoryPath); + return filesDirectoryPath; +} + +string GetUnusedBlobId(DB db) { + string id() => RandomString.Generate(3); + var res = id(); + while (db.Files.Any(c => c.PublicId == res)) { + res = id(); + } + + return res; +} + +class BlobBase +{ + public string Password { get; set; } + public bool Singleton { get; set; } + public string AutoDeleteAfter { get; set; } +} + +class PasteRequest : BlobBase +{ + public string Text { get; set; } + public string Mime { get; set; } +} + +class UploadRequest : BlobBase +{ + public IFormFile? File { get; set; } +} \ No newline at end of file diff --git a/src/Properties/launchSettings.json b/src/Properties/launchSettings.json new file mode 100644 index 0000000..e19510b --- /dev/null +++ b/src/Properties/launchSettings.json @@ -0,0 +1,15 @@ +{ + "$schema": "https://json.schemastore.org/launchsettings.json", + "profiles": { + "http": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": false, + "launchUrl": "", + "applicationUrl": "http://localhost:5033", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } +} diff --git a/src/wwwroot/index.html b/src/wwwroot/index.html new file mode 100644 index 0000000..fa25951 --- /dev/null +++ b/src/wwwroot/index.html @@ -0,0 +1,80 @@ + + + + + + Blobbin + + +

Blobbin

+

This is a web service you can upload files and texts to.

+
+
+ Upload a file +
+ + + + + + + +
+
+
+ Upload some text +
+ + + + + + + + + +
+
+
+ + \ No newline at end of file -- cgit v1.3