chore: remove rails app

This commit is contained in:
Juan Rodriguez
2024-05-12 15:07:56 +02:00
parent 8eb27f2c8a
commit 720b70c6a0
101 changed files with 0 additions and 10276 deletions
@@ -1,7 +0,0 @@
# frozen_string_literal: true
class ApplicationController < ActionController::Base
def authenticate
@current_user = User.find_by(id: session[:user_id])
end
end
-49
View File
@@ -1,49 +0,0 @@
# frozen_string_literal: true
class LinksController < ApplicationController
include LinksHelper
before_action :authenticate, only: %i[create]
before_action :set_link, only: %i[redirect counter]
def redirect
if @link
@link.increment!(:click_counter) # rubocop:disable Rails/SkipsModelValidations
redirect_to @link.parsed_url
else
render file: Rails.root.join('/public/404'), status: :not_found
end
end
def counter
if @link
render json: @link.click_counter
else
render json: nil, status: :not_found
end
end
def create
url = stripped_url(link_params[:url])
@link = Link.find_or_create_by(url: url) do |link|
link.user = @current_user if @current_user
end
if @link.errors.any?
render json: @link.errors, status: :unprocessable_entity
else
render partial: 'links/show', locals: { link: @link }, status: :ok
end
end
private
def set_link
@link = Link.find_by(slug: params[:slug])
end
def link_params
params.require(:link).permit(:url)
end
end
-31
View File
@@ -1,31 +0,0 @@
# frozen_string_literal: true
class SessionsController < ApplicationController
before_action :authenticate, except: %i[create]
before_action :set_user, only: %i[create]
def create
if @user&.authenticate(session_params[:password])
session[:user_id] = @user.id
session[:username] = @user.username
render json: nil, status: :ok
else
render json: { username: ['Credentials not valid, try again or create an account'] }, status: :unauthorized
end
end
def destroy
reset_session
render json: nil, status: :ok
end
private
def set_user
@user = User.find_by(username: session_params[:username])
end
def session_params
params.permit(:username, :password)
end
end
-30
View File
@@ -1,30 +0,0 @@
# frozen_string_literal: true
class UsersController < ApplicationController
around_action :confirm_password_validation, only: %i[create]
def create
@user = User.create(user_params)
if @user.errors.any?
render json: @user.errors, status: :unprocessable_entity
else
session[:user_id] = @user.id
session[:username] = @user.username
render json: nil, status: :ok
end
end
private
def confirm_password_validation
if user_params[:password] == params[:user][:confirm_password]
yield
else
render json: { password: ['Password not match with Confirm Password'] }, status: :bad_request
end
end
def user_params
params.require(:user).permit(:username, :password)
end
end
-7
View File
@@ -1,7 +0,0 @@
# frozen_string_literal: true
module LinksHelper
def stripped_url(url)
url.sub(%r{^.*://(www\.)?}, '').sub(/^www\./, '')
end
end
-7
View File
@@ -1,7 +0,0 @@
# frozen_string_literal: true
module SessionsHelper
def current_user_username
session[:username]
end
end
-10
View File
@@ -1,10 +0,0 @@
// Load all the controllers within this directory and all subdirectories.
// Controller files must be named *_controller.js.
import { Application } from "stimulus"
import { definitionsFromContext } from "stimulus/webpack-helpers"
const application = Application.start()
const context = require.context("controllers", true, /_controller\.js$/)
application.load(definitionsFromContext(context))
@@ -1,60 +0,0 @@
import { Controller } from "stimulus"
import Turbolinks from "turbolinks"
export default class extends Controller {
static targets = ["url", "output", "userLinks"]
initialize() {
this.lastLink = null
this.sessionUsername = localStorage.getItem("session-username")
}
onCreateLinkSuccess(event) {
const [, , xhr] = event.detail
this.outputTarget.innerHTML = xhr.response
if (this.sessionUsername && this.lastLink && this.lastLink.includes(this.sessionUsername)
&& !this.userLinksTarget.innerHTML.includes(this.lastLink)) {
this.userLinksTarget.innerHTML = this.lastLink + this.userLinksTarget.innerHTML
}
this.lastLink = xhr.response
}
onCreateLinkError(event) {
const [data, ,] = event.detail
const urlError = `Url: ${data.url.join(' ')}`
alert(urlError)
}
async updateLinkCounter(counterElem, slug) {
const clickCounter = await fetch(`links/${slug}/counter`, {
headers: {
'X-CSRF-Token': document.querySelector('meta[name="csrf-token"]').getAttribute('content')
}
}).then((res) => res.json());
if (clickCounter) {
counterElem.innerText = clickCounter
}
}
openLink(event) {
const counterElem = event.target.parentElement.parentElement.parentElement.getElementsByClassName("counter")[0]
const link = event.target.innerText
let visibilitychange = 0
const visibilitychangeListener = () => {
if(visibilitychange > 0) {
document.removeEventListener("visibilitychange", visibilitychangeListener)
this.updateLinkCounter(counterElem, link.substring(link.lastIndexOf('/') + 1))
return
}
visibilitychange += 1
}
document.addEventListener("visibilitychange", visibilitychangeListener)
window.open(link, "_blank")
}
}
@@ -1,61 +0,0 @@
import { Controller } from "stimulus"
import Turbolinks from "turbolinks"
export default class extends Controller {
static targets = ["signupModal", "loginModal"]
openLoginModal() {
this.loginModalTarget.classList.remove("hidden")
}
closeLoginModal() {
this.loginModalTarget.classList.add("hidden")
}
openSignupModal() {
this.closeLoginModal()
this.signupModalTarget.classList.remove("hidden")
}
closeSignupModal() {
this.signupModalTarget.classList.add("hidden")
}
onSignupSuccess() {
this.closeSignupModal();
Turbolinks.visit('/')
}
onLoginSuccess() {
this.closeLoginModal();
Turbolinks.visit('/')
}
onError(event) {
const [data, ,] = event.detail
const errors = []
if (data.username) {
errors.push(`Username: ${data.username.join(' ')}`)
}
if (data.password) {
errors.push(`Password: ${data.password.join(' ')}`)
}
alert(errors.join(','))
}
confirmLogout(event) {
if (!window.confirm("Do you really want to leave?")) {
event.stopPropagation()
return
}
}
onSuccessLogout() {
localStorage.removeItem("session-username")
Turbolinks.visit('/')
}
}
-5
View File
@@ -1,5 +0,0 @@
@import "tailwindcss/base";
@import "tailwindcss/components";
@import "tailwindcss/utilities";
@import "../stylesheets/base.css";
-23
View File
@@ -1,23 +0,0 @@
/* eslint no-console:0 */
// This file is automatically compiled by Webpack, along with any other files
// present in this directory. You're encouraged to place your actual application logic in
// a relevant structure within app/javascript and only use these pack files to reference
// that code so it'll be compiled.
//
// To reference this file, add <%= javascript_pack_tag 'application' %> to the appropriate
// layout file, like app/views/layouts/application.html.erb
// Uncomment to copy all static images under ../images to the output folder and reference
// them with the image_pack_tag helper in views (e.g <%= image_pack_tag 'rails.png' %>)
// or the `imagePath` JavaScript helper below.
//
// const images = require.context('../images', true)
// const imagePath = (name) => images(name, true)
require("@rails/ujs").start()
require("turbolinks").start()
import "controllers"
import "./application.css"
-3
View File
@@ -1,3 +0,0 @@
a, button, input[type=submit] {
cursor: pointer;
}
-5
View File
@@ -1,5 +0,0 @@
# frozen_string_literal: true
class ApplicationRecord < ActiveRecord::Base
self.abstract_class = true
end
-34
View File
@@ -1,34 +0,0 @@
# frozen_string_literal: true
class Link < ApplicationRecord
validates :url, presence: true
validates :slug, uniqueness: true
validates :url,
format: {
with: %r{\A(?:https?://)?(?:\w+\.)?(?:google\.com|sjdonado\.de)(?:/[\w-]+)*/?\z},
message: 'domains allowed for demo purposes: [google.com, sjdonado.de]'
}
validates :url, length: { within: 3..30_000, on: :create, message: 'max length is 30000' }
before_validation :generate_slug
def parsed_url
"https://#{url}"
end
def generate_slug(attempts = 0)
return if slug.present? || attempts == 3
# Number of combinations 62P6
generated_slug = SecureRandom.alphanumeric(6)
if Link.exists?(slug: generated_slug)
generate_slug(attempts + 1)
else
self.slug = generated_slug
end
end
belongs_to :user, optional: true
end
-8
View File
@@ -1,8 +0,0 @@
# frozen_string_literal: true
class User < ApplicationRecord
validates :username, uniqueness: true
has_secure_password
has_many :links, dependent: :nullify
end
-10
View File
@@ -1,10 +0,0 @@
<div data-users-target="<%= target %>" class="fixed z-10 inset-0 overflow-y-auto hidden" aria-labelledby="modal-title" role="dialog" aria-modal="true">
<div class="flex items-end justify-center min-h-screen pt-4 px-4 pb-20 text-center sm:block sm:p-0">
<div class="fixed inset-0 bg-gray-500 bg-opacity-75 transition-opacity" aria-hidden="true"></div>
<!-- This element is to trick the browser into centering the modal contents. -->
<span class="hidden sm:inline-block sm:align-middle sm:h-screen" aria-hidden="true">&#8203;</span>
<%= yield %>
</div>
</div>
-33
View File
@@ -1,33 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<html>
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>URL Shortener</title>
<meta name="description" content="Tailwind + Stimulus + Rails 5 app">
<meta name="keywords" content="url-shortener, shortener, url">
<meta property="og:title" content="URL Shortener">
<meta property="og:type" content="website">
<meta property="og:url" content="https://url-shortener.sjdonado.de">
<meta property="og:site_name" content="URL Shortener">
<meta property="og:description" content="Tailwind + Stimulus + Rails 5 app">
<meta property="og:image" content="https://user-images.githubusercontent.com/27580836/227800665-4ff7e2ae-8189-4593-8961-496b7c9ac861.png">
<meta property="og:image:alt" content="URL Shortener screenshot">
<%= csrf_meta_tags %>
<%= csp_meta_tag %>
<%= stylesheet_pack_tag 'application', media: 'all', 'data-turbolinks-track': 'reload' %>
<%= javascript_pack_tag 'application', 'data-turbolinks-track': 'reload' %>
</head>
<body>
<%= yield %>
</body>
</html>
-14
View File
@@ -1,14 +0,0 @@
<%= form_with model: Link.new, url: links_path(@link), data: { action: 'ajax:success->links#onCreateLinkSuccess ajax:error->links#onCreateLinkError' } do |f| %>
<div class="col-span-3 sm:col-span-2">
<label for="company_website" class="text-lg leading-6 font-medium text-gray-900">
URL Shortener
</label>
<div class="mt-4 flex rounded-md shadow-sm">
<%= f.text_field :url, pattern: '(https?:\/\/)?(www\.)?([a-zA-Z0-9_-]+\.)+[a-zA-Z]{2,}(\.[a-zA-Z]{2,})?([\/?#][^\s]*)?', data: { target: "links.url" }, placeholder: "https://google.com", class: "focus:ring-indigo-500 focus:border-indigo-500 flex-1 block w-full rounded-none rounded-md sm:text-sm border-gray-300" %>
</div>
</div>
<div class="py-3 text-right">
<%= f.submit "Shorten", class: "inline-flex w-full sm:w-auto justify-center py-2 px-4 border border-transparent shadow-sm text-sm font-medium rounded-md text-white bg-indigo-600 hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500" %>
</div>
<% end %>
-8
View File
@@ -1,8 +0,0 @@
<% unless @current_user.nil? %>
<h2 class="text-2xl leading-6 font-medium text-gray-900 mx-4 my-6">My links</h2>
<div data-links-target="userLinks">
<% @current_user.links.order(created_at: :desc).each do |link| %>
<%= render partial: "links/show", locals: { link: link } %>
<% end %>
</div>
<% end %>
-4
View File
@@ -1,4 +0,0 @@
<div class="bg-white shadow overflow-hidden sm:rounded-lg m-3 p-6">
<%= render partial: "links/form" %>
</div>
<div data-links-target="output" class="my-6"></div>
-38
View File
@@ -1,38 +0,0 @@
<div class="bg-white shadow overflow-hidden sm:rounded-lg m-3">
<div class="border-t border-gray-200">
<dl>
<div class="bg-gray-50 px-4 py-4 sm:grid sm:grid-cols-3 sm:gap-4 sm:px-6">
<dt class="text-sm font-medium text-gray-500">
Website
</dt>
<dd class="mt-1 text-sm text-gray-900 sm:mt-0 sm:col-span-2">
<a href="<%= link.parsed_url %>" class="mt-1 text-sm text-gray-600 underline" target="_blank" rel="noreferrer"><%= link.url %></a>
</dd>
</div>
<div class="bg-white px-4 py-4 sm:grid sm:grid-cols-3 sm:gap-4 sm:px-6">
<dt class="text-sm font-medium text-gray-500">
Shortened URL
</dt>
<dd class="mt-1 text-sm text-gray-900 sm:mt-0 sm:col-span-2">
<button data-action="click->links#openLink" class="mt-1 text-sm text-gray-600 underline"><%= short_url(slug: link.slug) %></button>
</dd>
</div>
<div class="bg-gray-50 px-4 py-4 sm:grid sm:grid-cols-3 sm:gap-4 sm:px-6">
<dt class="text-sm font-medium text-gray-500">
Click counter
</dt>
<dd class="counter mt-1 text-sm text-gray-900 sm:mt-0 sm:col-span-2">
<%= link.click_counter %>
</dd>
</div>
<div class="bg-white px-4 py-4 sm:grid sm:grid-cols-3 sm:gap-4 sm:px-6">
<dt class="text-sm font-medium text-gray-500">
Created by
</dt>
<dd class="counter mt-1 text-sm text-gray-900 sm:mt-0 sm:col-span-2">
<%= link.user ? link.user.username : 'Guest user' %>
</dd>
</div>
</dl>
</div>
</div>
-34
View File
@@ -1,34 +0,0 @@
<%= form_with url: '/login', data: { action: 'ajax:success->users#onLoginSuccess ajax:error->users#onError' }, class: "inline-block align-bottom bg-white rounded-lg text-left overflow-hidden shadow-xl transform transition-all sm:my-8 sm:align-middle sm:max-w-lg sm:w-full" do |f| %>
<div class="bg-white px-4 pt-5 pb-4 sm:p-6 sm:pb-4">
<div class="sm:flex sm:items-start">
<div class="flex-1 mt-3 text-center sm:mt-0 sm:mx-4 sm:text-left">
<h3 class="text-xl leading-6 font-medium text-gray-900">
Login
</h3>
<div class="flex-col mt-4">
<div class="my-2">
<label for="username" class="block text-sm font-medium text-gray-700 text-left">
Username
</label>
<%= f.text_field :username, class: "focus:ring-indigo-500 focus:border-indigo-500 flex-1 block w-full rounded-none rounded-md sm:text-sm border-gray-300" %>
</div>
<div class="my-2">
<label for="password" class="block text-sm font-medium text-gray-700 text-left">
Password
</label>
<div class="mt-1 flex rounded-md shadow-sm">
<%= f.password_field :password, class: "focus:ring-indigo-500 focus:border-indigo-500 flex-1 block w-full rounded-none rounded-md sm:text-sm border-gray-300" %>
</div>
</div>
</div>
</div>
</div>
<button type="button" data-action="click->users#openSignupModal" class="block w-full text-sm font-medium sm:pl-4 pt-6 underline text-center sm:text-left">Create an account</button>
</div>
<div class="bg-gray-50 px-4 py-3 sm:px-6 sm:flex sm:flex-row-reverse">
<%= f.submit "Login", class: "my-1 mt-3 w-full inline-flex justify-center rounded-md border border-gray-300 shadow-sm px-4 py-2 bg-white text-base font-medium text-gray-700 hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 sm:mt-0 sm:ml-3 sm:w-auto sm:text-sm" %>
<button data-action="click->users#closeLoginModal" type="button" class="my-1 sm:mt-0 w-full inline-flex justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-red-600 text-base font-medium text-white hover:bg-red-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-red-500 sm:ml-3 sm:w-auto sm:text-sm">
Cancel
</button>
</div>
<%end%>
-21
View File
@@ -1,21 +0,0 @@
<div data-controller="users">
<div class="bg-gray-50 px-4 py-3 sm:px-6 sm:flex sm:flex-row-reverse">
<% if @current_user.nil? %>
<button data-action="click->users#openLoginModal" class="mt-3 w-full inline-flex justify-center rounded-md border border-gray-300 shadow-sm px-4 py-2 bg-white text-base font-medium text-gray-700 hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 sm:mt-0 sm:ml-3 sm:w-auto sm:text-sm">
Login
</button>
<% end %>
<%= render partial: "users/show" %>
</div>
<%= render partial: "users/new", layout: "layouts/modal", locals: { target: 'signupModal' } %>
<%= render partial: "sessions/new", layout: "layouts/modal", locals: { target: 'loginModal' } %>
</div>
<div data-controller="links">
<%= render partial: "links/new" %>
<%= render partial: "links/index" %>
</div>
<% unless @current_user.nil? %>
<script>localStorage.setItem("session-username", "<%= current_user_username %>");</script>
<% end %>
-39
View File
@@ -1,39 +0,0 @@
<%= form_with model: User.new, url: users_path(@user), data: { action: 'ajax:success->users#onSignupSuccess ajax:error->users#onError' }, class: "inline-block align-bottom bg-white rounded-lg text-left overflow-hidden shadow-xl transform transition-all sm:my-8 sm:align-middle sm:max-w-lg sm:w-full" do |f| %>
<div class="bg-white px-4 pt-5 pb-4 sm:p-6 sm:pb-4">
<div class="flex-1 mt-3 text-center sm:mt-0 sm:mx-4 sm:text-left">
<h3 class="text-xl leading-6 font-medium text-gray-900">
Sign Up
</h3>
<div class="flex-col mt-4">
<div class="my-2">
<label for="username" class="block text-sm font-medium text-gray-700 text-left">
Username
</label>
<%= f.text_field :username, class: "focus:ring-indigo-500 focus:border-indigo-500 flex-1 block w-full rounded-none rounded-md sm:text-sm border-gray-300" %>
</div>
<div class="my-2">
<label for="password" class="block text-sm font-medium text-gray-700 text-left">
Password
</label>
<div class="mt-1 flex rounded-md shadow-sm">
<%= f.password_field :password, class: "focus:ring-indigo-500 focus:border-indigo-500 flex-1 block w-full rounded-none rounded-md sm:text-sm border-gray-300" %>
</div>
</div>
<div class="my-2">
<label for="confirm_password" class="block text-sm font-medium text-gray-700 text-left">
Confirm password
</label>
<div class="mt-1 flex rounded-md shadow-sm">
<%= f.password_field :confirm_password, class: "focus:ring-indigo-500 focus:border-indigo-500 flex-1 block w-full rounded-none rounded-md sm:text-sm border-gray-300" %>
</div>
</div>
</div>
</div>
</div>
<div class="bg-gray-50 px-4 py-3 sm:px-6 sm:flex sm:flex-row-reverse">
<%= f.submit "Sign Up", class: "my-1 mt-3 w-full inline-flex justify-center rounded-md border border-gray-300 shadow-sm px-4 py-2 bg-white text-base font-medium text-gray-700 hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 sm:mt-0 sm:ml-3 sm:w-auto sm:text-sm" %>
<button data-action="click->users#closeSignupModal" type="button" class="my-1 sm:mt-0 w-full inline-flex justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-red-600 text-base font-medium text-white hover:bg-red-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-red-500 sm:ml-3 sm:w-auto sm:text-sm">
Cancel
</button>
</div>
<%end%>
-4
View File
@@ -1,4 +0,0 @@
<% unless @current_user.nil? %>
<%= link_to "Logout", '/session/logout', data: { action: 'ajax:before->users#confirmLogout ajax:success->users#onSuccessLogout' }, remote: true, class: "mt-3 w-full inline-flex justify-center rounded-md border border-gray-300 shadow-sm px-4 py-2 bg-white text-base font-medium text-gray-700 hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 sm:mt-0 sm:ml-3 sm:w-auto sm:text-sm" %>
<h1 class="px-2 py-2 text-sm font-medium">You are Logged In, <%= @current_user.username %></h1>
<%end%>