Merge branch '@feature/proper_auth' into develop

This commit is contained in:
Fergal Moran
2023-01-24 01:28:22 +00:00
69 changed files with 150416 additions and 696 deletions

View File

@@ -1,25 +0,0 @@
**/.dockerignore
**/.env
**/.git
**/.gitignore
**/.project
**/.settings
**/.toolstarget
**/.vs
**/.vscode
**/.idea
**/*.*proj.user
**/*.dbmdl
**/*.jfm
**/azds.yaml
**/bin
**/charts
**/docker-compose*
**/Dockerfile*
**/node_modules
**/npm-debug.log
**/obj
**/secrets.dev.yaml
**/values.dev.yaml
LICENSE
README.md

5
.env.development Normal file
View File

@@ -0,0 +1,5 @@
NEXTAUTH_SECRET=FOo84lSFySlsU19WXv+RWE2N54PyeigBMJWxAiZucw0=
NEXTAUTH_URL=https://nukeitter.dev.fergl.ie:3000
TWITTER_CLIENT_ID=U1VDNWl0eHNhLW1MN2lDN1gyOGI6MTpjaQ
TWITTER_CLIENT_SECRET=TNwtd1iNnwU9uZeZWd7k7DtNQN1z0X0fCljvxd-lLHGhm3U9Nb

3
.eslintrc.json Normal file
View File

@@ -0,0 +1,3 @@
{
"extends": "next/core-web-vitals"
}

41
.gitignore vendored
View File

@@ -1,5 +1,36 @@
bin/
obj/
/packages/
riderModule.iml
/_ReSharper.Caches/
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
# dependencies
/node_modules
/.pnp
.pnp.js
# testing
/coverage
# next.js
/.next/
/out/
# production
/build
# misc
.DS_Store
*.pem
# debug
npm-debug.log*
yarn-debug.log*
yarn-error.log*
.pnpm-debug.log*
# local env files
.env*.local
# vercel
.vercel
# typescript
*.tsbuildinfo
next-env.d.ts

View File

@@ -1,13 +0,0 @@
# Default ignored files
/shelf/
/workspace.xml
# Rider ignored files
/projectSettingsUpdater.xml
/modules.xml
/contentModel.xml
/.idea.nukeitter-web.iml
# Editor-based HTTP Client requests
/httpRequests/
# Datasource local storage ignored files
/dataSources/
/dataSources.local.xml

View File

@@ -1,4 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="Encoding" addBOMForNewFiles="with BOM under Windows, with no BOM otherwise" />
</project>

View File

@@ -1,8 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="UserContentModel">
<attachedFolders />
<explicitIncludes />
<explicitExcludes />
</component>
</project>

View File

@@ -1,6 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="" vcs="Git" />
</component>
</project>

View File

@@ -1,13 +0,0 @@
# Default ignored files
/shelf/
/workspace.xml
# Rider ignored files
/projectSettingsUpdater.xml
/modules.xml
/contentModel.xml
/.idea.nukitter-web.iml
# Editor-based HTTP Client requests
/httpRequests/
# Datasource local storage ignored files
/dataSources/
/dataSources.local.xml

View File

@@ -1,4 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="Encoding" addBOMForNewFiles="with BOM under Windows, with no BOM otherwise" />
</project>

View File

@@ -1,8 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="UserContentModel">
<attachedFolders />
<explicitIncludes />
<explicitExcludes />
</component>
</project>

View File

@@ -1,6 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="JavaScriptLibraryMappings">
<file url="PROJECT" libraries="{daisyui}" />
</component>
</project>

View File

@@ -1,6 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="" vcs="Git" />
</component>
</project>

16
.vscode/launch.json vendored Normal file
View File

@@ -0,0 +1,16 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"type": "node",
"request": "launch",
"name": "Launch Program",
"skipFiles": ["<node_internals>/**"],
"program": "${workspaceFolder}/server.js",
"outFiles": ["${workspaceFolder}/**/*.js"]
}
]
}

4
.vscode/settings.json vendored Normal file
View File

@@ -0,0 +1,4 @@
{
"typescript.tsdk": "node_modules/typescript/lib",
"typescript.enablePromptUseWorkspaceTsdk": true
}

147529
.yarn/releases/yarn-1.22.19.cjs vendored Executable file

File diff suppressed because one or more lines are too long

1
.yarnrc.yml Normal file
View File

@@ -0,0 +1 @@
yarnPath: .yarn/releases/yarn-1.22.19.cjs

View File

@@ -1,14 +0,0 @@
<CascadingAuthenticationState>
<Router AppAssembly="@typeof(App).Assembly">
<Found Context="routeData">
<RouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)"/>
<FocusOnNavigate RouteData="@routeData" Selector="h1"/>
</Found>
<NotFound>
<PageTitle>Not found</PageTitle>
<LayoutView Layout="@typeof(MainLayout)">
<p role="alert">Sorry, there's nothing at this address.</p>
</LayoutView>
</NotFound>
</Router>
</CascadingAuthenticationState>

View File

@@ -1,16 +0,0 @@
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.Twitter;
using Microsoft.AspNetCore.Mvc;
namespace Nukitter.Web.Controllers;
[ApiController]
[Route("[controller]")]
public class UserController : ControllerBase {
[HttpGet("twitter")]
public async Task TwitterSignIn() {
await HttpContext.ChallengeAsync(TwitterDefaults.AuthenticationScheme, new AuthenticationProperties {
RedirectUri = "https://nukeitter.dev.fergl.ie:5001/profile"
});
}
}

View File

@@ -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 ["nukitter-web.csproj", "./"]
RUN dotnet restore "nukitter-web.csproj"
COPY . .
WORKDIR "/src/"
RUN dotnet build "nukitter-web.csproj" -c Release -o /app/build
FROM build AS publish
RUN dotnet publish "nukitter-web.csproj" -c Release -o /app/publish
FROM base AS final
WORKDIR /app
COPY --from=publish /app/publish .
ENTRYPOINT ["dotnet", "nukitter-web.dll"]

View File

@@ -1,18 +0,0 @@
@page "/counter"
<PageTitle>Counter</PageTitle>
<h1>Counter</h1>
<p role="status">Current count: @currentCount</p>
<button class="btn btn-primary" @onclick="IncrementCount">Click me</button>
@code {
private int currentCount = 0;
private void IncrementCount() {
currentCount++;
}
}

View File

@@ -1,41 +0,0 @@
@page
@model Nukitter.Web.Pages.ErrorModel
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no"/>
<title>Error</title>
<link href="~/css/bootstrap/bootstrap.min.css" rel="stylesheet"/>
<link href="~/css/site.css" rel="stylesheet" asp-append-version="true"/>
</head>
<body>
<div class="main">
<div class="content px-4">
<h1 class="text-danger">Error.</h1>
<h2 class="text-danger">An error occurred while processing your request.</h2>
@if (Model.ShowRequestId) {
<p>
<strong>Request ID:</strong> <code>@Model.RequestId</code>
</p>
}
<h3>Development Mode</h3>
<p>
Swapping to the <strong>Development</strong> environment displays detailed information about the error that occurred.
</p>
<p>
<strong>The Development environment shouldn't be enabled for deployed applications.</strong>
It can result in displaying sensitive information from exceptions to end users.
For local debugging, enable the <strong>Development</strong> environment by setting the <strong>ASPNETCORE_ENVIRONMENT</strong> environment variable to <strong>Development</strong>
and restarting the app.
</p>
</div>
</div>
</body>
</html>

View File

@@ -1,23 +0,0 @@
using System.Diagnostics;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
namespace Nukitter.Web.Pages;
[ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
[IgnoreAntiforgeryToken]
public class ErrorModel : PageModel {
public string? RequestId { get; set; }
public bool ShowRequestId => !string.IsNullOrEmpty(RequestId);
private readonly ILogger<ErrorModel> _logger;
public ErrorModel(ILogger<ErrorModel> logger) {
_logger = logger;
}
public void OnGet() {
RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier;
}
}

View File

@@ -1,37 +0,0 @@
@page "/"
<PageTitle>Twit Nuke</PageTitle>
<div
class="container mx-auto flex flex-col md:flex-row items-center my-12 md:my-24">
<!--Left Col-->
<div
class="flex flex-col w-full lg:w-1/2 justify-center items-start pt-12 pb-24 px-6 group">
<p class="uppercase tracking-loose">Nukeitter</p>
<div class="group flex justify-center items-center">
<h1 class="font-bold text-3xl my-4 flx flex-row">
Delete your Tweets on a schedule... or just once.
</h1>
</div>
<p class="leading-normal mb-4">
You never know who's gonna read that radioactive Tweet you forgot you sent
in 2017
</p>
<a
href="/setup"
class="btn btn-outline btn-info gap-2">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-6 h-6">
<path stroke-linecap="round" stroke-linejoin="round" d="M15.59 14.37a6 6 0 01-5.84 7.38v-4.8m5.84-2.58a14.98 14.98 0 006.16-12.12A14.98 14.98 0 009.631 8.41m5.96 5.96a14.926 14.926 0 01-5.841 2.58m-.119-8.54a6 6 0 00-7.381 5.84h4.8m2.581-5.84a14.927 14.927 0 00-2.58 5.84m2.699 2.7c-.103.021-.207.041-.311.06a15.09 15.09 0 01-2.448-2.448 14.9 14.9 0 01.06-.312m-2.24 2.39a4.493 4.493 0 00-1.757 4.306 4.493 4.493 0 004.306-1.758M16.5 9a1.5 1.5 0 11-3 0 1.5 1.5 0 013 0z"/>
</svg>
Let's go
</a>
</div>
<!--Right Col-->
<div class="w-full lg:w-1/2 lg:py-6 text-center">
<img
src="/img/logo.svg"
alt="Radioactive"
class="fill-current text-gray-900 w-3/5 mx-auto"/>
</div>
</div>

View File

@@ -1,31 +0,0 @@
@page "/profile"
@using System.Security.Claims
@inject NavigationManager _navigationManager
<PageTitle>Twit Nuke</PageTitle>
<div
class="container mx-auto flex flex-col md:flex-row items-center my-12 md:my-24">
<h1>This is your profile</h1>
<div>@claim</div>
</div>
@code {
[CascadingParameter]
public Task<AuthenticationState> authenticationState { get; set; }
public Claim claim;
protected override async Task OnInitializedAsync() {
var authState = await authenticationState;
var user = authState.User;
if (user.Identity.IsAuthenticated) {
claim = user.FindFirst(c => c.Type == ClaimTypes.NameIdentifier);
}
else {
_navigationManager.NavigateTo("/");
}
}
}

View File

@@ -1,42 +0,0 @@
@page "/setup"
@inject NavigationManager _navigationManager
<PageTitle>Twit Nuke</PageTitle>
<div
class="container mx-auto flex flex-col md:flex-row items-center my-12 md:my-24">
<!--Left Col-->
<div
class="flex flex-col w-full lg:w-1/2 justify-center items-start pt-12 pb-24 px-6 group">
<div class="group flex justify-center items-center">
<h1 class="font-bold text-3xl my-4 flx flex-row">
Let's get started
</h1>
</div>
<p class="leading-normal mb-4">
We need access to your Twitter account, promise we won't do anything icky!!
</p>
<button
@onclick="TwitterSignin"
href="/setup"
class="btn btn-outline btn-info gap-2">
<i class="fa-brands fa-twitter"></i>
Login to Twitter
</button>
</div>
<!--Right Col-->
<div class="w-full lg:w-1/2 lg:py-6 text-center">
<img
src="/img/logo.svg"
alt="Radioactive"
class="fill-current text-gray-900 w-3/5 mx-auto"/>
</div>
</div>
@code {
private void TwitterSignin() {
_navigationManager.NavigateTo("user/twitter", true);
}
}

View File

@@ -1,35 +0,0 @@
@page "/"
@using Microsoft.AspNetCore.Components.Web
@namespace Nukitter.Web.Pages
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
@addTagHelper *, Heroicons.AspNetCore.Mvc.TagHelpers
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<base href="~/"/>
<link href="https://cdn.jsdelivr.net/npm/daisyui@2.47.0/dist/full.css" rel="stylesheet" type="text/css"/>
<script src="https://cdn.tailwindcss.com"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.2.1/css/all.min.css" integrity="sha512-MV7K8+y+gLIBoVD59lQIYicR65iaqukzvf/nwasF0nqhPay5w/9lJmVM2hMDcnK1OnMGCdVK+iQrJ7lzPJQd1w==" crossorigin="anonymous" referrerpolicy="no-referrer"/>
<link rel="icon" type="image/png" href="favicon.ico"/>
<component type="typeof(HeadOutlet)" render-mode="ServerPrerendered"/>
</head>
<body>
<component type="typeof(App)" render-mode="ServerPrerendered"/>
<div id="blazor-error-ui">
<environment include="Staging,Production">
An error has occurred. This application may no longer respond until reloaded.
</environment>
<environment include="Development">
An unhandled exception has occurred. See browser dev tools for details.
</environment>
<a href="" class="reload">Reload</a>
<a class="dismiss">🗙</a>
</div>
<script src="_framework/blazor.server.js"></script>
</body>
</html>

View File

@@ -1,47 +0,0 @@
using System.Net;
using System.Security.Cryptography.X509Certificates;
using Microsoft.AspNetCore.Authentication.Cookies;
var builder = WebApplication.CreateBuilder(args);
builder.WebHost.ConfigureKestrel(options => {
options.Listen(IPAddress.Any, 5001, listenOptions => {
var certPem = File.ReadAllText("/etc/letsencrypt/live/dev.fergl.ie/fullchain.pem");
var keyPem = File.ReadAllText("/etc/letsencrypt/live/dev.fergl.ie/privkey.pem");
var x509 = X509Certificate2.CreateFromPem(certPem, keyPem);
listenOptions.UseHttps(x509);
});
});
builder.Services.AddRazorPages();
builder.Services.AddServerSideBlazor();
builder.Services.AddAuthentication(options => {
options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
})
.AddCookie()
.AddTwitter(twitterOptions => {
twitterOptions.ConsumerKey = builder.Configuration["Auth:Twitter:ConsumerKey"];
twitterOptions.ConsumerSecret = builder.Configuration["Auth:Twitter:ConsumerSecret"];
twitterOptions.RetrieveUserDetails = true;
});
builder.Services.AddControllers();
var app = builder.Build();
if (!app.Environment.IsDevelopment()) {
app.UseExceptionHandler("/Error");
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.MapBlazorHub();
app.MapFallbackToPage("/_Host");
app.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
app.Run();

View File

@@ -1,37 +0,0 @@
{
"iisSettings": {
"windowsAuthentication": false,
"anonymousAuthentication": true,
"iisExpress": {
"applicationUrl": "http://localhost:33728",
"sslPort": 44341
}
},
"profiles": {
"http": {
"commandName": "Project",
"dotnetRunMessages": true,
"launchBrowser": false,
"applicationUrl": "http://localhost:5000",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"https": {
"commandName": "Project",
"dotnetRunMessages": true,
"launchBrowser": false,
"applicationUrl": "https://localhost:5001",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"IIS Express": {
"commandName": "IISExpress",
"launchBrowser": true,
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
}
}
}

38
README.md Normal file
View File

@@ -0,0 +1,38 @@
This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app).
## Getting Started
First, run the development server:
```bash
npm run dev
# or
yarn dev
# or
pnpm dev
```
Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file.
[API routes](https://nextjs.org/docs/api-routes/introduction) can be accessed on [http://localhost:3000/api/hello](http://localhost:3000/api/hello). This endpoint can be edited in `pages/api/hello.ts`.
The `pages/api` directory is mapped to `/api/*`. Files in this directory are treated as [API routes](https://nextjs.org/docs/api-routes/introduction) instead of React pages.
This project uses [`next/font`](https://nextjs.org/docs/basic-features/font-optimization) to automatically optimize and load Inter, a custom Google Font.
## Learn More
To learn more about Next.js, take a look at the following resources:
- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome!
## Deploy on Vercel
The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.
Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details.

View File

@@ -1,15 +0,0 @@
@inherits LayoutComponentBase
<PageTitle>nukitter-web</PageTitle>
<div class="page">
<div class="sidebar">
<NavMenu/>
</div>
<main>
<article class="content px-4">
@Body
</article>
</main>
</div>

View File

@@ -1,70 +0,0 @@
.page {
position: relative;
display: flex;
flex-direction: column;
}
main {
flex: 1;
}
.sidebar {
background-image: linear-gradient(180deg, rgb(5, 39, 103) 0%, #3a0647 70%);
}
.top-row {
background-color: #f7f7f7;
border-bottom: 1px solid #d6d5d5;
justify-content: flex-end;
height: 3.5rem;
display: flex;
align-items: center;
}
.top-row ::deep a, .top-row .btn-link {
white-space: nowrap;
margin-left: 1.5rem;
}
.top-row a:first-child {
overflow: hidden;
text-overflow: ellipsis;
}
@media (max-width: 640.98px) {
.top-row:not(.auth) {
display: none;
}
.top-row.auth {
justify-content: space-between;
}
.top-row a, .top-row .btn-link {
margin-left: 0;
}
}
@media (min-width: 641px) {
.page {
flex-direction: row;
}
.sidebar {
width: 250px;
height: 100vh;
position: sticky;
top: 0;
}
.top-row {
position: sticky;
top: 0;
z-index: 1;
}
.top-row, article {
padding-left: 2rem !important;
padding-right: 1.5rem !important;
}
}

View File

@@ -1,14 +0,0 @@
<div class="navbar bg-base-100">
<div class="flex-none">
<img
src="/img/logo.svg"
alt="Radioactive"
class="fill-current text-gray-900 w-8 h-8 mx-auto"/>
</div>
<div class="flex-1">
<a class="btn btn-ghost normal-case text-xl">NukeTwitter</a>
</div>
<div class="flex-none">
<span>Stuff on the right</span>
</div>
</div>

View File

@@ -1,68 +0,0 @@
.navbar-toggler {
background-color: rgba(255, 255, 255, 0.1);
}
.top-row {
height: 3.5rem;
background-color: rgba(0,0,0,0.4);
}
.navbar-brand {
font-size: 1.1rem;
}
.oi {
width: 2rem;
font-size: 1.1rem;
vertical-align: text-top;
top: -2px;
}
.nav-item {
font-size: 0.9rem;
padding-bottom: 0.5rem;
}
.nav-item:first-of-type {
padding-top: 1rem;
}
.nav-item:last-of-type {
padding-bottom: 1rem;
}
.nav-item ::deep a {
color: #d7d7d7;
border-radius: 4px;
height: 3rem;
display: flex;
align-items: center;
line-height: 3rem;
}
.nav-item ::deep a.active {
background-color: rgba(255,255,255,0.25);
color: white;
}
.nav-item ::deep a:hover {
background-color: rgba(255,255,255,0.1);
color: white;
}
@media (min-width: 641px) {
.navbar-toggler {
display: none;
}
.collapse {
/* Never collapse the sidebar for wide screens */
display: block;
}
.nav-scrollable {
/* Allow sidebar to scroll for tall menus */
height: calc(100vh - 3.5rem);
overflow-y: auto;
}
}

View File

@@ -1,17 +0,0 @@
<div class="alert alert-secondary mt-4">
<span class="oi oi-pencil me-2" aria-hidden="true"></span>
<strong>@Title</strong>
<span class="text-nowrap">
Please take our
<a target="_blank" class="font-weight-bold link-dark" href="https://go.microsoft.com/fwlink/?linkid=2186158">brief survey</a>
</span>
and tell us what you think.
</div>
@code {
// Demonstrates how a parent component can supply parameters
[Parameter]
public string? Title { get; set; }
}

View File

@@ -1,10 +0,0 @@
@using System.Net.Http
@using Microsoft.AspNetCore.Authorization
@using Microsoft.AspNetCore.Components.Authorization
@using Microsoft.AspNetCore.Components.Forms
@using Microsoft.AspNetCore.Components.Routing
@using Microsoft.AspNetCore.Components.Web
@using Microsoft.AspNetCore.Components.Web.Virtualization
@using Microsoft.JSInterop
@using Nukitter.Web
@using Nukitter.Web.Shared

View File

@@ -1,9 +0,0 @@
{
"DetailedErrors": true,
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
}
}

View File

@@ -1,15 +0,0 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*",
"Auth": {
"Twitter": {
"ConsumerKey": "wRE6ajw7rraqDax8iOhZCHvml",
"ConsumerSecret": "l4Jn42ON1BrN8N4L28unQJYcjcRpbWuMIfcwzZZx3Dg8ZipyoD"
}
}
}

8
next.config.js Normal file
View File

@@ -0,0 +1,8 @@
/** @type {import('next').NextConfig} */
const nextConfig = {
experimental: {
appDir: true,
},
}
module.exports = nextConfig

View File

@@ -1,19 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net7.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<RootNamespace>Nukitter.Web</RootNamespace>
<DockerDefaultTargetOS>Linux</DockerDefaultTargetOS>
</PropertyGroup>
<ItemGroup>
<Folder Include="Data" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Authentication.Twitter" Version="7.0.2" />
</ItemGroup>
</Project>

32
package.json Normal file
View File

@@ -0,0 +1,32 @@
{
"name": "nukeitter",
"version": "0.1.0",
"private": true,
"scripts": {
"dev": "node ./server.js",
"build": "next build",
"start": "next start",
"lint": "next lint"
},
"dependencies": {
"@next/font": "13.1.5",
"@types/node": "18.11.18",
"@types/react": "18.0.27",
"@types/react-dom": "18.0.10",
"eslint": "8.32.0",
"eslint-config-next": "13.1.5",
"next": "13.1.5",
"next-auth": "^4.18.8",
"react": "18.2.0",
"react-dom": "18.2.0",
"react-icons": "^4.7.1",
"typescript": "4.9.4"
},
"devDependencies": {
"autoprefixer": "^10.4.13",
"daisyui": "^2.47.0",
"postcss": "^8.4.21",
"tailwindcss": "^3.2.4"
},
"packageManager": "yarn@1.22.19"
}

6
postcss.config.js Normal file
View File

@@ -0,0 +1,6 @@
module.exports = {
plugins: {
tailwindcss: {},
autoprefixer: {},
},
}

View File

Before

Width:  |  Height:  |  Size: 20 KiB

After

Width:  |  Height:  |  Size: 20 KiB

View File

Before

Width:  |  Height:  |  Size: 54 KiB

After

Width:  |  Height:  |  Size: 54 KiB

View File

Before

Width:  |  Height:  |  Size: 18 KiB

After

Width:  |  Height:  |  Size: 18 KiB

View File

Before

Width:  |  Height:  |  Size: 774 B

After

Width:  |  Height:  |  Size: 774 B

View File

Before

Width:  |  Height:  |  Size: 2.0 KiB

After

Width:  |  Height:  |  Size: 2.0 KiB

View File

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 15 KiB

View File

Before

Width:  |  Height:  |  Size: 62 KiB

After

Width:  |  Height:  |  Size: 62 KiB

View File

Before

Width:  |  Height:  |  Size: 5.1 KiB

After

Width:  |  Height:  |  Size: 5.1 KiB

23
server.js Normal file
View File

@@ -0,0 +1,23 @@
const https = require("https");
const fs = require("fs");
const next = require("next");
const port = 3000;
const dev = process.env.NODE_ENV !== "production";
const hostname = "nukeitter.dev.fergl.ie";
const app = next({ dev, hostname, port, dir: __dirname });
const handle = app.getRequestHandler();
const options = {
key: fs.readFileSync("/etc/letsencrypt/live/dev.fergl.ie/privkey.pem"),
cert: fs.readFileSync("/etc/letsencrypt/live/dev.fergl.ie/fullchain.pem"),
};
app.prepare().then(() => {
https
.createServer(options, (req, res) => handle(req, res))
.listen(port, (err) => {
if (err) throw err;
console.log(`> Ready on localhost:${port}`);
});
});

3
src/app/globals.css Normal file
View File

@@ -0,0 +1,3 @@
@tailwind base;
@tailwind components;
@tailwind utilities;

10
src/app/head.tsx Normal file
View File

@@ -0,0 +1,10 @@
export default function Head() {
return (
<>
<title>NukeItter</title>
<meta content="width=device-width, initial-scale=1" name="viewport" />
<meta name="description" content="Delete Your Tweets!!" />
<link rel="icon" href="/favicon.ico" />
</>
)
}

25
src/app/layout.tsx Normal file
View File

@@ -0,0 +1,25 @@
"use client";
import NavbarComponent from "@/components/layout/NavBar";
import { SessionProvider } from "next-auth/react";
import "./globals.css";
export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<html lang="en" data-theme="cupcake">
<head />
<body>
<SessionProvider>
<NavbarComponent />
<main>
<article className="px-4 content">{children}</article>
</main>
</SessionProvider>
</body>
</html>
);
}

0
src/app/page.module.css Normal file
View File

23
src/app/page.tsx Normal file
View File

@@ -0,0 +1,23 @@
"use client";
import { useSession } from "next-auth/react";
import { LoginPage, LoggedInPage } from "@/components/pages";
import { LoadingSpinner } from "@/components/widgets";
export default function Home() {
const session = useSession();
const _getPage = () => {
switch (session.status) {
case "authenticated":
return <LoggedInPage />;
case "loading":
return <LoadingSpinner />;
case "unauthenticated":
return <LoginPage />;
}
};
return (
<div className="container flex flex-col items-center mx-auto my-12 md:flex-row md:my-24">
{_getPage()}
</div>
);
}

11
src/app/setup/page.tsx Normal file
View File

@@ -0,0 +1,11 @@
import React from "react";
const SetupPage = () => {
return (
<div className="container flex flex-col items-center mx-auto my-4 md:flex-row md:my-8">
SetupPage
</div>
);
};
export default SetupPage;

View File

@@ -0,0 +1,23 @@
import React from "react";
const NavbarComponent = () => {
return (
<div className="navbar bg-base-100">
<div className="flex-none">
<img
src="/img/logo.svg"
alt="Radioactive"
className="w-8 h-8 mx-auto text-gray-900 fill-current"
/>
</div>
<div className="flex-1">
<a className="text-xl normal-case btn btn-ghost">NukeTwitter</a>
</div>
<div className="flex-none">
<span>Stuff on the right</span>
</div>
</div>
);
};
export default NavbarComponent;

View File

@@ -0,0 +1,9 @@
import React from "react";
import { RiTwitterFill } from "react-icons/ri";
import { signIn } from "next-auth/react";
const LoginPage = () => {
return <React.Fragment>Good boy</React.Fragment>;
};
export default LoginPage;

View File

@@ -0,0 +1,38 @@
import React from "react";
import { RiTwitterFill } from "react-icons/ri";
import { signIn } from "next-auth/react";
const LoginPage = () => {
return (
<React.Fragment>
<div className="flex flex-col items-start justify-center w-full px-6 pt-12 pb-24 lg:w-1/2 group">
<p className="uppercase tracking-loose">Nukeitter</p>
<div className="flex items-center justify-center group">
<h1 className="flex-row my-4 text-3xl font-bold flx">
Delete your Tweets on a schedule... or just once.
</h1>
</div>
<p className="mb-4 leading-normal">
You never know who&apos;s gonna read that radioactive Tweet you forgot
you sent in 2017
</p>
<button
className="gap-2 btn btn-outline btn-info"
onClick={() => signIn("twitter")}
>
<RiTwitterFill className="w-6 h-6" />
Sign in with Twitter
</button>
</div>
<div className="w-full text-center lg:w-1/2 lg:py-6">
<img
src="/img/logo.svg"
alt="Radioactive"
className="w-3/5 mx-auto text-gray-900 fill-current"
/>
</div>
</React.Fragment>
);
};
export default LoginPage;

View File

@@ -0,0 +1,4 @@
import LoggedInPage from "./LoggedInPage";
import LoginPage from "./LoginPage";
export { LoggedInPage, LoginPage };

View File

@@ -0,0 +1,27 @@
import React from "react";
const LoadingSpinner = () => {
return (
<div role="status">
<svg
aria-hidden="true"
className="w-8 h-8 mr-2 text-gray-200 animate-spin dark:text-gray-600 fill-blue-600"
viewBox="0 0 100 101"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M100 50.5908C100 78.2051 77.6142 100.591 50 100.591C22.3858 100.591 0 78.2051 0 50.5908C0 22.9766 22.3858 0.59082 50 0.59082C77.6142 0.59082 100 22.9766 100 50.5908ZM9.08144 50.5908C9.08144 73.1895 27.4013 91.5094 50 91.5094C72.5987 91.5094 90.9186 73.1895 90.9186 50.5908C90.9186 27.9921 72.5987 9.67226 50 9.67226C27.4013 9.67226 9.08144 27.9921 9.08144 50.5908Z"
fill="currentColor"
/>
<path
d="M93.9676 39.0409C96.393 38.4038 97.8624 35.9116 97.0079 33.5539C95.2932 28.8227 92.871 24.3692 89.8167 20.348C85.8452 15.1192 80.8826 10.7238 75.2124 7.41289C69.5422 4.10194 63.2754 1.94025 56.7698 1.05124C51.7666 0.367541 46.6976 0.446843 41.7345 1.27873C39.2613 1.69328 37.813 4.19778 38.4501 6.62326C39.0873 9.04874 41.5694 10.4717 44.0505 10.1071C47.8511 9.54855 51.7191 9.52689 55.5402 10.0491C60.8642 10.7766 65.9928 12.5457 70.6331 15.2552C75.2735 17.9648 79.3347 21.5619 82.5849 25.841C84.9175 28.9121 86.7997 32.2913 88.1811 35.8758C89.083 38.2158 91.5421 39.6781 93.9676 39.0409Z"
fill="currentFill"
/>
</svg>
<span className="sr-only">Loading...</span>
</div>
);
};
export default LoadingSpinner;

View File

@@ -0,0 +1,3 @@
import LoadingSpinner from "./LoadingSpinner";
export { LoadingSpinner };

View File

@@ -0,0 +1,15 @@
import NextAuth from "next-auth";
import TwitterProvider from "next-auth/providers/twitter";
export const authOptions = {
// Configure one or more authentication providers
providers: [
TwitterProvider({
clientId: process.env.TWITTER_CLIENT_ID,
clientSecret: process.env.TWITTER_CLIENT_SECRET,
version: "2.0",
}),
],
};
export default NextAuth(authOptions);

13
src/pages/api/hello.ts Normal file
View File

@@ -0,0 +1,13 @@
// Next.js API route support: https://nextjs.org/docs/api-routes/introduction
import type { NextApiRequest, NextApiResponse } from 'next'
type Data = {
name: string
}
export default function handler(
req: NextApiRequest,
res: NextApiResponse<Data>
) {
res.status(200).json({ name: 'John Doe' })
}

12
tailwind.config.js Normal file
View File

@@ -0,0 +1,12 @@
/** @type {import('tailwindcss').Config} */
module.exports = {
content: [
"./src/**/*.{js,ts,jsx,tsx}",
"./public/**/*.html",
],
darkMode: "media",
theme: {
extend: {},
},
plugins: [require("daisyui")],
};

29
tsconfig.json Normal file
View File

@@ -0,0 +1,29 @@
{
"compilerOptions": {
"target": "es5",
"lib": ["dom", "dom.iterable", "esnext"],
"allowJs": true,
"skipLibCheck": true,
"strict": true,
"forceConsistentCasingInFileNames": true,
"noEmit": true,
"esModuleInterop": true,
"module": "esnext",
"moduleResolution": "node",
"resolveJsonModule": true,
"isolatedModules": true,
"jsx": "preserve",
"incremental": true,
"plugins": [
{
"name": "next"
}
],
"baseUrl": ".",
"paths": {
"@/*": ["./src/*"]
}
},
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
"exclude": ["node_modules"]
}

2470
yarn.lock Normal file

File diff suppressed because it is too large Load Diff